الكتابة إلى الملفات في بايثون: دليل شامل لدوال الفتح، القراءة، الإضافة، والمزيد
في هذا المقال، ستتعلم:
- كيفية فتح ملف.
- كيفية قراءة محتوى ملف.
- كيفية إنشاء ملفات جديدة.
- كيفية تعديل محتوى ملف موجود.
- كيفية إغلاق الملفات بشكل صحيح.
- كيفية فتح الملفات لعمليات متعددة في آن واحد.
- كيفية الاستفادة من دوال كائنات الملفات.
- كيفية حذف الملفات برمجياً.
- كيفية استخدام مديري السياق (
Context Managers) وسبب أهميتها. - كيفية التعامل مع الاستثناءات والأخطاء المحتملة عند العمل مع الملفات.
- والمزيد من النصائح القيمة!
لنبدأ رحلتنا الآن! ✨
التعامل مع الملفات في بايثون: الأساسيات
تُعد الدالة open() من أهم الدوال المدمجة في بايثون والتي ستحتاج إليها باستمرار عند التعامل مع الملفات. تقوم هذه الدالة بفتح ملف وتجهيزه لبرنامجك للتعامل معه. إليك البنية الأساسية لاستخدامها:

💡 نصيحة: المعاملان المذكوران أعلاه هما الأكثر استخدامًا. توجد ستة معاملات اختيارية إضافية يمكنك استكشافها في توثيقات بايثون الرسمية لمزيد من التفاصيل.
المعامل الأول: المسار (file)
المعامل الأول للدالة open() هو file، والذي يمثل المسار المطلق أو النسبي للملف الذي ترغب في التعامل معه. عادةً ما نستخدم المسار النسبي، الذي يحدد موقع الملف بالنسبة لموقع السكريبت (ملف بايثون) الذي يستدعي الدالة open(). على سبيل المثال، المسار في هذا الاستدعاء:
open("names.txt")
# المسار النسبي هو "names.txt"
يحتوي هذا المسار على اسم الملف فقط، ويُستخدم عندما يكون الملف المراد فتحه في نفس المجلد أو الدليل الذي يحتوي على سكريبت بايثون، كما هو موضح هنا:

ولكن إذا كان الملف داخل مجلد متداخل، مثل هذا الترتيب:

حيث يوجد ملف names.txt داخل مجلد "data"، فنحن بحاجة إلى استخدام مسار محدد لإخبار الدالة بأن الملف يقع داخل مجلد آخر. في هذا المثال، سيكون المسار كالتالي:
open("data/names.txt")
لاحظ أننا نكتب data/ أولاً (اسم المجلد متبوعًا بعلامة /) ثم names.txt (اسم الملف مع امتداده).
💡 نصيحة: الأحرف الثلاثة .txt التي تتبع النقطة في names.txt هي “امتداد” الملف، أو نوعه. في هذه الحالة، يشير .txt إلى أنه ملف نصي.
المعامل الثاني: الوضع (mode)
المعامل الثاني للدالة open() هو mode، وهو عبارة عن سلسلة نصية تحتوي على حرف واحد يخبر بايثون بالعملية التي تنوي القيام بها على الملف في برنامجك. الأوضاع المتاحة هي:
- القراءة (
"r"). - الإضافة (
"a"). - الكتابة (
"w"). - الإنشاء (
"x").
يمكنك أيضًا اختيار فتح الملف في:
- الوضع النصي (
"t"). - الوضع الثنائي (
"b").
لاستخدام الوضع النصي أو الثنائي، ستحتاج إلى إضافة هذه الأحرف إلى الوضع الرئيسي. على سبيل المثال: "wb" يعني الكتابة في الوضع الثنائي.
💡 نصيحة: الأوضاع الافتراضية هي القراءة ("r") والنص ("t")، مما يعني “الفتح لقراءة النص” ("rt"). لذلك، لا تحتاج إلى تحديدها في open() إذا كنت ترغب في استخدامها لأنها تُعيّن افتراضيًا. يمكنك ببساطة كتابة open(<file>).
لماذا توجد أوضاع مختلفة؟
من المنطقي أن تمنح بايثون أذونات معينة فقط بناءً على ما تخطط للقيام به مع الملف، أليس كذلك؟ لماذا يجب أن تسمح بايثون لبرنامجك بالقيام بأكثر مما هو ضروري؟ هذا هو السبب الأساسي لوجود الأوضاع. فكر في الأمر – السماح لبرنامج بالقيام بأكثر مما هو ضروري يمكن أن يكون مشكلة. على سبيل المثال، إذا كنت تحتاج فقط إلى قراءة محتوى ملف، فقد يكون من الخطير السماح لبرنامجك بتعديله بشكل غير متوقع، مما قد يؤدي إلى إدخال أخطاء.
كيفية قراءة ملف في بايثون
الآن بعد أن عرفت المزيد عن المعاملات التي تأخذها الدالة open()، دعنا نرى كيف يمكنك فتح ملف وتخزينه في متغير لاستخدامه في برنامجك. هذه هي البنية الأساسية:

نحن ببساطة نقوم بتعيين القيمة المُرجعة إلى متغير. على سبيل المثال:
names_file = open("data/names.txt", "r")
قد تتساءل: ما نوع القيمة التي تُرجعها الدالة open()؟ إنها كائن ملف (file object). دعنا نتحدث قليلًا عن هذه الكائنات.
كائنات الملفات (File Objects)
وفقًا لتوثيقات بايثون، كائن الملف هو:
كائن يعرض واجهة برمجة تطبيقات موجهة للملفات (مع دوال مثل
read()أوwrite()) لمورد أساسي.
هذا يعني ببساطة أن كائن الملف هو كائن يسمح لنا بالعمل والتفاعل مع الملفات الموجودة في برنامج بايثون الخاص بنا. تحتوي كائنات الملفات على سمات (attributes)، مثل:
name: اسم الملف.closed:Trueإذا كان الملف مغلقًا، وFalseبخلاف ذلك.mode: الوضع المستخدم لفتح الملف.

على سبيل المثال:
f = open("data/names.txt", "a")
print(f.mode) # الناتج: "a"
الآن دعنا نرى كيف يمكنك الوصول إلى محتوى ملف من خلال كائن ملف.
دوال قراءة الملفات
لكي نتمكن من العمل مع كائنات الملفات، نحتاج إلى طريقة “للتفاعل” معها في برنامجنا، وهذا بالضبط ما تفعله الدوال (methods). دعنا نرى بعضًا منها.
الدالة read()
أول دالة تحتاج إلى معرفتها هي read()، والتي تُرجع المحتوى الكامل للملف كسلسلة نصية (string).

إليك مثال:
f = open("data/names.txt")
print(f.read())
الناتج هو:
Nora
Gino
Timmy
William
يمكنك استخدام الدالة type() لتأكيد أن القيمة المُرجعة بواسطة f.read() هي سلسلة نصية:
print(type(f.read())) # الناتج <class 'str'>
نعم، إنها سلسلة نصية! في هذه الحالة، تم طباعة الملف بأكمله لأننا لم نحدد عددًا أقصى من البايتات، ولكن يمكننا القيام بذلك أيضًا. إليك مثال:
f = open("data/names.txt")
print(f.read(3))
القيمة المُرجعة محدودة بهذا العدد من البايتات:
Nor
❗️ هام: يجب عليك إغلاق الملف بعد اكتمال المهمة لتحرير الموارد المرتبطة بالملف. للقيام بذلك، تحتاج إلى استدعاء الدالة close()، هكذا:

مقارنة بين readline() و readlines()
يمكنك قراءة ملف سطرًا بسطر باستخدام هاتين الدالتين. إنهما تختلفان قليلًا، لذا دعنا نراهما بالتفصيل.
الدالة readline()
تقرأ الدالة readline() سطرًا واحدًا من الملف حتى تصل إلى نهاية هذا السطر. ويتم الاحتفاظ بحرف السطر الجديد اللاحق (\n) في السلسلة النصية.
💡 نصيحة: اختياريًا، يمكنك تمرير الحجم (size)، وهو الحد الأقصى لعدد الأحرف التي تريد تضمينها في السلسلة النصية الناتجة.

على سبيل المثال:
f = open("data/names.txt")
print(f.readline())
f.close()
الناتج هو:
Nora
هذا هو السطر الأول من الملف.
الدالة readlines()
على النقيض، تُرجع الدالة readlines() قائمة (list) تحتوي على جميع أسطر الملف كعناصر فردية (سلاسل نصية). هذه هي البنية:

على سبيل المثال:
f = open("data/names.txt")
print(f.readlines())
f.close()
الناتج هو:
['Nora\n', 'Gino\n', 'Timmy\n', 'William']
لاحظ وجود \n (حرف السطر الجديد) في نهاية كل سلسلة نصية، باستثناء الأخيرة.
💡 نصيحة: يمكنك الحصول على نفس القائمة باستخدام list(f). يمكنك العمل مع هذه القائمة في برنامجك عن طريق تعيينها لمتغير أو استخدامها في حلقة تكرارية:
f = open("data/names.txt")
for line in f.readlines():
# قم بعمل شيء مع كل سطر
f.close()
يمكننا أيضًا التكرار مباشرة على f (كائن الملف) في حلقة:
f = open("data/names.txt", "r")
for line in f:
# قم بعمل شيء مع كل سطر
f.close()
هذه هي الدوال الرئيسية المستخدمة لقراءة كائنات الملفات. الآن دعنا نرى كيف يمكنك إنشاء ملفات.
كيفية إنشاء ملف جديد
إذا كنت بحاجة إلى إنشاء ملف “ديناميكيًا” باستخدام بايثون، يمكنك القيام بذلك باستخدام الوضع "x". دعنا نرى كيف. هذه هي البنية الأساسية:

إليك مثال. هذا هو دليل العمل الحالي الخاص بي:

إذا قمت بتشغيل سطر الكود هذا:
f = open("new_file.txt", "x")
سيتم إنشاء ملف جديد بهذا الاسم:

باستخدام هذا الوضع، يمكنك إنشاء ملف ثم الكتابة فيه ديناميكيًا باستخدام الدوال التي ستتعلمها بعد قليل.
💡 نصيحة: سيكون الملف فارغًا في البداية حتى تقوم بتعديله. الشيء المثير للاهتمام هو أنه إذا حاولت تشغيل هذا السطر مرة أخرى وكان هناك ملف بهذا الاسم موجود بالفعل، فسترى هذا الخطأ:
Traceback (most recent call last):
File "<path>", line 8, in <module>
f = open("new_file.txt", "x")
FileExistsError: [Errno 17] File exists: 'new_file.txt'
وفقًا لتوثيقات بايثون، هذا الاستثناء (خطأ وقت التشغيل) هو:
يُثار عند محاولة إنشاء ملف أو دليل موجود بالفعل.
الآن بعد أن عرفت كيفية إنشاء ملف، دعنا نرى كيف يمكنك تعديله.
كيفية تعديل ملف في بايثون
لتعديل (الكتابة إلى) ملف، تحتاج إلى استخدام الدالة write(). لديك طريقتان للقيام بذلك (الإضافة أو الكتابة) بناءً على الوضع الذي تختاره لفتح الملف به. دعنا نراهما بالتفصيل.
وضع الإضافة (Append - "a")
“الإضافة” تعني إضافة شيء إلى نهاية شيء آخر. يسمح لك الوضع "a" بفتح ملف لإضافة بعض المحتوى إليه. على سبيل المثال، إذا كان لدينا هذا الملف:

وإذا أردنا إضافة سطر جديد إليه، يمكننا فتحه باستخدام الوضع "a" (إضافة) ثم استدعاء الدالة write()، وتمرير المحتوى الذي نريد إضافته كمعامل. هذه هي البنية الأساسية لاستدعاء الدالة write():

إليك مثال:
f = open("data/names.txt", "a")
f.write("\nNew Line")
f.close()
💡 نصيحة: لاحظ أنني أضفت \n قبل السطر للإشارة إلى أنني أريد أن يظهر السطر الجديد كسطر منفصل، وليس كاستمرار للسطر الحالي. هذا هو الملف الآن، بعد تشغيل السكريبت:

💡 نصيحة: قد لا يظهر السطر الجديد في الملف حتى يتم تشغيل f.close().
وضع الكتابة (Write - "w")
أحيانًا، قد ترغب في حذف محتوى ملف بالكامل واستبداله بمحتوى جديد. يمكنك القيام بذلك باستخدام الدالة write() إذا فتحت الملف بالوضع "w". إليك هذا الملف النصي:

إذا قمت بتشغيل هذا السكريبت:
f = open("data/names.txt", "w")
f.write("New Content")
f.close()
هذه هي النتيجة:

كما ترى، فإن فتح ملف بالوضع "w" ثم الكتابة فيه يستبدل المحتوى الموجود بالكامل.
💡 نصيحة: تُرجع الدالة write() عدد الأحرف المكتوبة. إذا كنت ترغب في كتابة عدة أسطر دفعة واحدة، يمكنك استخدام الدالة writelines()، والتي تأخذ قائمة من السلاسل النصية. تمثل كل سلسلة نصية سطرًا ليتم إضافته إلى الملف. إليك مثال. هذا هو الملف الأولي:

إذا قمنا بتشغيل هذا السكريبت:
f = open("data/names.txt", "a")
f.writelines(["\nline1", "\nline2", "\nline3"])
f.close()
تتم إضافة الأسطر إلى نهاية الملف:

فتح الملفات لعمليات متعددة
الآن أنت تعرف كيفية إنشاء ملف وقراءته والكتابة فيه، ولكن ماذا لو أردت القيام بأكثر من شيء واحد في نفس البرنامج؟ دعنا نرى ما يحدث إذا حاولنا القيام بذلك باستخدام الأوضاع التي تعلمتها حتى الآن:
إذا فتحت ملفًا في الوضع "r" (قراءة)، ثم حاولت الكتابة فيه:
f = open("data/names.txt")
f.write("New Content") # محاولة الكتابة
f.close()
ستحصل على هذا الخطأ:
Traceback (most recent call last):
File "<path>", line 9, in <module>
f.write("New Content")
io.UnsupportedOperation: not writable
وبالمثل، إذا فتحت ملفًا في الوضع "w" (كتابة)، ثم حاولت قراءته:
f = open("data/names.txt", "w")
print(f.readlines()) # محاولة القراءة
f.write("New Content")
f.close()
سترى هذا الخطأ:
Traceback (most recent call last):
File "<path>", line 14, in <module>
print(f.readlines())
io.UnsupportedOperation: not readable
سيحدث نفس الشيء مع الوضع "a" (إضافة). كيف يمكننا حل هذه المشكلة؟
للقراءة من ملف وتنفيذ عملية أخرى في نفس البرنامج، تحتاج إلى إضافة الرمز "+" إلى الوضع، هكذا:
f = open("data/names.txt", "w+") # قراءة + كتابة
f = open("data/names.txt", "a+") # قراءة + إضافة
f = open("data/names.txt", "r+") # قراءة + كتابة
مفيد جدًا، أليس كذلك؟ هذا هو ما ستستخدمه غالبًا في برامجك، ولكن تأكد من تضمين الأوضاع التي تحتاجها فقط لتجنب الأخطاء المحتملة. أحيانًا لا تكون الملفات مطلوبة بعد الآن. دعنا نرى كيف يمكنك حذف الملفات باستخدام بايثون.
كيفية حذف الملفات في بايثون
لإزالة ملف باستخدام بايثون، تحتاج إلى استيراد وحدة تسمى os، والتي تحتوي على دوال تتفاعل مع نظام التشغيل الخاص بك.
💡 نصيحة: الوحدة (module) هي ملف بايثون يحتوي على متغيرات ودوال وفئات ذات صلة. على وجه الخصوص، تحتاج إلى الدالة remove(). تأخذ هذه الدالة مسار الملف كمعامل وتحذف الملف تلقائيًا.

دعنا نرى مثالًا. نريد إزالة الملف المسمى sample_file.txt.

للقيام بذلك، نكتب هذا الكود:
import os
os.remove("sample_file.txt")
السطر الأول: import os يسمى “عبارة استيراد”. تُكتب هذه العبارة في الجزء العلوي من ملفك وتمنحك الوصول إلى الدوال المعرفة في الوحدة os. السطر الثاني: os.remove("sample_file.txt") يزيل الملف المحدد.
💡 نصيحة: يمكنك استخدام مسار مطلق أو نسبي. الآن بعد أن عرفت كيفية حذف الملفات، دعنا نرى أداة مثيرة للاهتمام… مديري السياق (Context Managers)!
مديرو السياق (Context Managers)
مديرو السياق هم هياكل بايثون ستجعل حياتك أسهل بكثير. باستخدامهم، لا تحتاج إلى تذكر إغلاق ملف في نهاية برنامجك، ولديك وصول إلى الملف في الجزء المحدد من البرنامج الذي تختاره.
البنية
هذا مثال على مدير سياق يستخدم للعمل مع الملفات:

💡 نصيحة: يجب أن تكون كتلة مدير السياق مزاحة، تمامًا كما نزيح الحلقات والدوال والفئات. إذا لم يكن الكود مزاحًا، فلن يعتبر جزءًا من مدير السياق. عندما تكتمل كتلة مدير السياق، يُغلق الملف تلقائيًا.
try:
# حاول تشغيل هذا الكود
except <type_of_exception>:
# إذا تم إثارة استثناء من هذا النوع، أوقف العملية وانتقل إلى هذه الكتلة
مثال
إليك مثال:
with open("data/names.txt", "r+") as f:
print(f.readlines())
يفتح مدير السياق هذا ملف names.txt لعمليات القراءة/الكتابة ويعين كائن الملف هذا للمتغير f. يُستخدم هذا المتغير في كتلة مدير السياق للإشارة إلى كائن الملف.
محاولة القراءة مرة أخرى
بعد اكتمال الكتلة، يُغلق الملف تلقائيًا، لذلك لا يمكن قراءته دون فتحه مرة أخرى. ولكن انتظر! لدينا سطر يحاول قراءته مرة أخرى، هنا في الأسفل:
with open("data/names.txt", "r+") as f:
print(f.readlines())
print(f.readlines()) # محاولة قراءة الملف مرة أخرى، خارج مدير السياق
دعنا نرى ما يحدث:
Traceback (most recent call last):
File "<path>", line 21, in <module>
print(f.readlines())
ValueError: I/O operation on closed file.
يُثار هذا الخطأ لأننا نحاول قراءة ملف مغلق. رائع، أليس كذلك؟ يقوم مدير السياق بكل العمل الشاق لنا، وهو قابل للقراءة، وموجز.
كيفية معالجة الاستثناءات عند العمل مع الملفات
عند العمل مع الملفات، يمكن أن تحدث أخطاء. أحيانًا قد لا تكون لديك الأذونات اللازمة لتعديل ملف أو الوصول إليه، أو قد لا يكون الملف موجودًا من الأساس. كمبرمج، تحتاج إلى توقع هذه الظروف والتعامل معها في برنامجك لتجنب الأعطال المفاجئة التي يمكن أن تؤثر بالتأكيد على تجربة المستخدم. دعنا نرى بعضًا من أكثر الاستثناءات شيوعًا (أخطاء وقت التشغيل) التي قد تجدها عند العمل مع الملفات:
FileNotFoundError
وفقًا لتوثيقات بايثون، هذا الاستثناء هو:
يُثار عندما يُطلب ملف أو دليل ولكنه غير موجود.
على سبيل المثال، إذا كان الملف الذي تحاول فتحه غير موجود في دليل العمل الحالي الخاص بك:
f = open("names.txt")
سترى هذا الخطأ:
Traceback (most recent call last):
File "<path>", line 8, in <module>
f = open("names.txt")
FileNotFoundError: [Errno 2] No such file or directory: 'names.txt'
دعنا نحلل هذا الخطأ سطرًا بسطر:
File "<path>", line 8, in <module>: يخبرك هذا السطر أن الخطأ قد أُثير عندما كان الكود في الملف الموجود في<path>قيد التشغيل. تحديدًا، عندما تم تنفيذline 8في<module>.f = open("names.txt"): هذا هو السطر الذي تسبب في الخطأ.FileNotFoundError: [Errno 2] No such file or directory: 'names.txt': يقول هذا السطر أن استثناءFileNotFoundErrorقد أُثير لأن الملف أو الدليلnames.txtغير موجود.
💡 نصيحة: بايثون وصفية جدًا في رسائل الأخطاء، أليس كذلك؟ هذه ميزة كبيرة أثناء عملية تصحيح الأخطاء.
PermissionError
هذا استثناء شائع آخر عند العمل مع الملفات. وفقًا لتوثيقات بايثون، هذا الاستثناء هو:
يُثار عند محاولة تشغيل عملية بدون حقوق الوصول الكافية – على سبيل المثال أذونات نظام الملفات.
يُثار هذا الاستثناء عندما تحاول قراءة أو تعديل ملف ليس لديك إذن بالوصول إليه. إذا حاولت القيام بذلك، فسترى هذا الخطأ:
Traceback (most recent call last):
File "<path>", line 8, in <module>
f = open("<file_path>")
PermissionError: [Errno 13] Permission denied: 'data'
IsADirectoryError
وفقًا لتوثيقات بايثون، هذا الاستثناء هو:
يُثار عندما تُطلب عملية ملف على دليل.
يُثار هذا الاستثناء تحديدًا عندما تحاول فتح أو العمل على دليل بدلاً من ملف، لذا كن حذرًا جدًا مع المسار الذي تمرره كمعامل.
كيفية معالجة الاستثناءات
لمعالجة هذه الاستثناءات، يمكنك استخدام عبارة try/except. باستخدام هذه العبارة، يمكنك “إخبار” برنامجك بما يجب فعله في حالة حدوث شيء غير متوقع. هذه هي البنية الأساسية:
try:
# حاول تشغيل هذا الكود
except <type_of_exception>:
# إذا تم إثارة استثناء من هذا النوع، أوقف العملية وانتقل إلى هذه الكتلة
هنا يمكنك رؤية مثال مع FileNotFoundError:
try:
f = open("names.txt")
except FileNotFoundError:
print("الملف غير موجود")
يقول هذا بشكل أساسي: حاول فتح الملف names.txt. إذا تم إثارة FileNotFoundError، فلا تتعطل! ببساطة اطبع عبارة وصفية للمستخدم.
💡 نصيحة: يمكنك اختيار كيفية التعامل مع الموقف عن طريق كتابة الكود المناسب في كتلة except. ربما يمكنك إنشاء ملف جديد إذا لم يكن موجودًا بالفعل. لإغلاق الملف تلقائيًا بعد المهمة (بغض النظر عما إذا كان قد تم إثارة استثناء أم لا في كتلة try) يمكنك إضافة كتلة finally.
try:
# حاول تشغيل هذا الكود
except <exception>:
# إذا تم إثارة هذا الاستثناء، أوقف العملية فورًا وانتقل إلى هذه الكتلة
finally:
# افعل هذا بعد تشغيل الكود، حتى لو تم إثارة استثناء
هذا مثال:
try:
f = open("names.txt")
except FileNotFoundError:
print("الملف غير موجود")
finally:
f.close()
هناك العديد من الطرق لتخصيص عبارة try/except/finally ويمكنك حتى إضافة كتلة else لتشغيل كتلة من الكود فقط إذا لم يتم إثارة أي استثناءات في كتلة try.
💡 نصيحة: لمعرفة المزيد حول معالجة الاستثناءات في بايثون، قد ترغب في قراءة مقالي: “كيفية معالجة الاستثناءات في بايثون: مقدمة مرئية مفصلة”.
الخلاصة التقنية
إن إتقان التعامل مع الملفات في بايثون هو حجر الزاوية لأي مطور يسعى لبناء تطبيقات قوية وموثوقة. لقد استعرضنا في هذا الدليل الشامل كيفية استخدام الدالة open() بمرونتها المتعددة الأوضاع (r، w، a، x، ودمجها مع +) لإجراء عمليات القراءة والكتابة والإضافة والإنشاء. تعلمنا أيضًا أهمية كائنات الملفات والدوال المتاحة مثل read()، readline()، readlines()، write()، و writelines()، وكيفية استخدام وحدة os لحذف الملفات.
الأهم من ذلك، سلطنا الضوء على الدور الحيوي لمديري السياق (with open(...) as f:) في تبسيط إدارة الملفات وضمان إغلاقها تلقائيًا، مما يجنبنا تسرب الموارد ومشاكل الأداء. كما تناولنا استراتيجيات معالجة الاستثناءات الشائعة مثل FileNotFoundError، PermissionError، و IsADirectoryError باستخدام كتل try/except/finally، وهو أمر بالغ الأهمية لبناء برامج مرنة ومستقرة. إن تطبيق هذه المفاهيم سيضمن لك إدارة فعالة وآمنة للملفات في مشاريع بايثون الخاصة بك، مما يعزز جودة الكود وتجربة المستخدم بشكل كبير.