قراءة الملفات والكتابة فيها في بايثون

من موسوعة حسوب

تتيح بايثون إمكانية الوصول إلى الملفات في نظام التشغيل وقرائتها والكتابة عليها دون الحاجة إلى استخدام مكتبات خارجية.

قراءة الملفات

تستخدم الدالة open()‎ لقراءة الملفات في بايثون، وتعيد هذه الدالة كائن file وتستخدم عادة مع وسيطين open(filename, mode)‎ كما هو موضح في المثال التالي:

>>> f = open('workfile', 'w')

الوسيط الأول هو سلسلة نصية تتضمن اسم الملف المراد فتحه. أما الوسيط الثاني فهو سلسلة نصية أيضًا تتضمن بضع حروف تصف طريقة استخدام الملف المفتوح. يمكن للوسيط الثاني mode أن يأخذ القيمة 'r' وعندها سيستخدم الملف المفتوح للقراءة فقط، ويأخذ القيمة 'w' ليكون الملف المفتوح للكتابة فقط (سيحذف التابع أي ملف يحمل الاسم نفسه)، ويأخذ القيمة 'a' للكتابة في نهاية الملف، ويأخذ القيمة ‎'r+'‎ لفتح الملف في نمطي القراءة والكتابة معًا.

الوسيط mode هو وسيط اختياري، ويأخذ القيمة 'r' في حال عدم تعيينه.

تفتح الملفات عادة في النمط النصي (text mode) والذي يعني قراءة السلاسل النصية وكتابتها من وإلى الملف، وتكون هذه السلاسل النصية مرمّزة بترميز معيّن، وفي حال عدم تعيين الترميز تأخذ السلاسل ترميزًا يعتمد على المنصّة التي تعمل عليها اللغة (راجع الدالة open()‎).

يؤدي إلحاق السلسلة النصية 'b' بالوسيط mode إلى فتح الملف في النمط الثنائي (binary mode)، وفي هذا النمط تُقرأ البيانات وتُكتب على هيئة كائنات ثنائية (bytes objects)، ويستخدم هذا النمط مع جميع الملفات التي لا تتضمّن نصوصًا.

في النمط النصي وعند قراءة الملفات يجري تحويل جميع رموز نهاية السطر (‎\n في يونكس، ‎\r\n في ويندوز) بصورة افتراضية إلى ‎\n. أما عند الكتابة إلى الملفات في هذا النمط، ترجع اللغة رموز ‎\n في الملف بأكمله وبصورة افتراضية إلى الرموز الخاصة بكل نظام تشغيل. 

لا تتسبّب عملية التعديل هذه بأي مشاكل في الملفات النصية، ولكنّها تؤدي إلى تلف البيانات الثنائية كما في ملفات ذات الصيغة JPEG أو EXE؛ لذا يجب الحرص على استخدام النمط الثنائي عند القراءة والكتابة من وإلى هذا النوع من الملفات.

ينصح باستخدام الكلمة المفتاحية with عند التعامل مع كائنات الملفات، ومن فوائدها أنّه سيُغلَق الملف بطريقة صحيحة بعد انتهاء تنفيذ الشيفرة البرمجية، حتى لو أُطلق استثناء في أي مرحلة من مراحل تنفيذ الشيفرة، إلى جانب أن استخدام with يختصر كتابة كتلة try-finally المكافئة:

>>> with open('workfile') as f:
...     read_data = f.read()
>>> f.closed
True

يجب استدعاء الدالة f.close()‎ في حال عدم استخدام الكلمة المفتاحية with وذلك لإغلاق الملف وتحرير مصادر النظام المستخدمة من قبل الملف. وإن لم يُغلق الملف باستخدام هذه الدالة فإنّ جامع القمامة في بايثون (Python's garbage collector) سيحذف الكائن في النهاية ويغلق الملف المفتوح، ولكن قد يبقى الملف مفتوحًا لفترة من الزمن قبل حدوث ذلك، وإلى جانب ذلك قد تقوم بعض أدوات بايثون بهذه المهمّة في أوقات مختلفة. لن يعود بالإمكان الوصول إلى الملف بعد إغلاقه سواء باستخدام عبارة with أو باستدعاء f.close()‎:

>>> f.close()
>>> f.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file

توابع كائنات الملفات

في الأمثلة التالية ضمن هذا القسم، سنفترض أنّنا قد أنشأنا مسبقًا كائن ملفّ يحمل الاسم f.

f.read()‎

لقراءة محتويات الملف يمكن استدعاء الدالة f.read(size)‎ والتي تقرأ كمية من البيانات وتعيدها على هيئة سلسلة نصية (في النمط النصي) أو كائنات ثنائية (في النمط الثنائي). الوسيط size هو وسيط رقمي اختياري. عند عدم استخدام هذا الوسيط أو عندما يأخذ قيمة سالبة، فإنّ الدالة ستقرأ وتعيد محتويات الملف بأكمله، ولكن يجب الانتباه إلى أنّه قد يكون حجم الملف المفتوح أكبر من الذاكرة المتوفّرة في الجهاز. أما عند تعيين قيمة الوسيط size تقرأ الدالة المقدار المطلوب من البايتات وتعيده، وعند الوصول إلى نهاية الملف تعيد الدالة f.read()‎ سلسلة نصية فارغة ('').

يوضح المثال التالي عمل هذه الدالة:

>>> f.read()
'This is the entire file.\n'
>>> f.read()
''

f.readline()‎

تقرأ الدالة f.readline()‎ سطرًا واحدًا من الملف، وتضيف الدالة محرف السطر الجديد (‎\n) في نهاية السلسلة النصية، ولا يضاف في السطر الأخير من الملف إن كان الملف لا ينتهي بمحرف السطر الجديد، ويساعد هذا على إزالة اللبس في القيمة المعادة، فلو أعادت f.readline()سلسلة نصية فارغة فهذا يعني أنّ الدالة قد وصلت إلى نهاية الملف، وإن كان السطر المقروء فارغًا فإنّ السلسلة المعادة ستحتوي على محرف السطر الجديد فقط.

>>> f.readline()
'This is the first line of the file.\n'
>>> f.readline()
'Second line of the file\n'
>>> f.readline()
''

لقراءة جميع الأسطر في الملف يمكن استخدام الحلقات التكرارية للمرور على كائن الملف، وتكون شيفرة التكرار بسيطة وسريعة وأقل استهلاكًا للذاكرة:

>>> for line in f:
...     print(line, end='')
...
This is the first line of the file.
Second line of the file

يمكن قراءة جميع أسطر الملف في قائمة يمكن استخدام list(f)‎ أو f.readlines()‎.

f.write()‎

يكتب التابع f.write(string)‎ محتويات الوسيط string في الملف ويعيد عدد الحروف المكتوبة.

>>> f.write('This is a test\n')
15

يجب تحويل أنواع البيانات الأخرى إما إلى سلسلة نصية (في النمط النصي) او كائنات ثنائية (في النمط الثنائي) لغرض كتابتها في الملف:

>>> value = ('the answer', 42)
>>> s = str(value)  # تحول الصف إلى سلسلة نصية
>>> f.write(s)
18

f.tell()‎

يعيد التابع f.tell()‎ عددًا صحيحًا يعطي الموقع الحالي لكائن الملف ضمن الملف المفتوح متمثّلًا بعدد البايتات من بداية الملف إلى الموقع الحالي في النمط الثنائي، وبعدد مبهم في النمط النصي.

ولتغيير الموقع الحالي في كائن الملف يمكن استخدام التابع f.seek(offset, from_what)‎. يُحسب الموقع بإضافة الوسيط offset إلى نقطة مرجعية، ويجري اختيار النقطة المرجعية بواسطة الوسيط from_what. تعني القيمة 0 أن عملية الحساب ستجري من بداية الملف، أما القيمة 1 فتعني استخدام الموقع الحالي في الملف كنقطة مرجعية لبدء الحساب، أما 2 فتعني أن النقطة المرجعية في نهاية الملف. يمكن الاستغناء عن قيمة from_what وستأخذ القيمة الافتراضية 0 وبذلك تأخذ الدالة بداية الملف كنقطة مرجعية.

>>> f = open('workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5)      # اذهب إلى البايت السادس في الملف
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2)  # اذهب إلى البايت الثالث قبل نهاية الملف
13
>>> f.read(1)
b'd'

تجري عملية البحث في الملفات النصية (أي تلك الملفات المفتوحة دون استخدام السلسلة النصية 'b') من بداية الملف فقط (يستثنى من ذلك البحث إلى نهاية الملف باستخدام الصيغة seek(0, 2)‎) ويأخذ الوسيط offset القيم المعادة من التابع f.tell()‎ أو الصفر فقط، ويؤدي استخدام أي قيم أخرى إلى سلوك غير مفهوم للدالة.

مصادر