قراءة الملفات
اقرأ الملفات النصية سطرًا بسطر باستخدام السياقات الآمنة.
- فتح الملفات وقراءتها باستخدام open() و with
- قراءة الملف بكامله أو سطرًا بسطر
- معالجة أخطاء الملفات الشائعة والعمل مع الترميز
- التكرار فوق الخطوط بكفاءة عبر الملفات الكبيرة
الجسرُ نحو القرص
البرامجُ التي لا تحسب إلا بما يكتبه المستخدمُ محبوسةٌ في الذاكرة. والملفاتُ تفتح الباب: الملفُ تسلسلُ أسطرٍ، وقراءتُه تَمشٍّ على ذلك التسلسل. أولُ خطوةٍ هي open()، التي ترجع كائنَ ملفٍ ملصوقًا بالباب:
f = open("data.txt", "r") # وضعُ القراءة
content = f.read()
f.close() # أغلق دائمًا عند الانتهاء!تعني "r" القراءةَ فقط. والانضباطُ ثقيل: يجب أن تجري close() حين تنتهي، وإلا تسرّب المعالجُ ، يبقى الملفُ محتجزًا زمنًا طويلًا بعد أن كففتَ عن حاجته. ونسيانُه هو جيلُ الأخطاءِ الأولُ في عالم الملفات.
جملة with: الإغلاقُ وعدًا
تجعل with الإغلاقَ تلقائيًّا، حتى إذا اقتحم خطأٌ المتبانةَ من وسطها:
with open("data.txt") as f:
content = f.read()
# يُغلق الملف هناتُعلن كتلةُ with عقدًا: افتحْه هنا، وسيُغلق عندما تنتهي هذه الكتلة ، بصورةٍ عاديةٍ أو استثناءٍ. حياةُ المعالجِ مؤطَّرةٌ في الكتلة، فلا يبقى ما يُنسى.
استراتيجياتُ القراءة
الملفُ الواحدُ بثلاثِ شهواتٍ:
# قراءة الملف بكامله كسلسلةٍ واحدة
with open("data.txt") as f:
text = f.read()
# قراءة سطرٍ بسطرٍ (موفّرةٍ للذاكرة مع الملفات الكبيرة)
with open("data.txt") as f:
for line in f:
print(line.rstrip()) # يُزيل السطر الجديد في النهاية
# قراءة كل الأسطر في قائمة
with open("data.txt") as f:
lines = f.readlines() # تتضمن \n في كل سلسلةيأخذ f.read() كلَّ شيءٍ دفعةً واحدةً؛ ويفرّق readlines() إلى قائمةٍ؛ والتكرارُ بـ for line in f يتمشّى في الملفِ سطرًا سطرًا، محتفظًا فقط بالسطرِ الحالي في الذاكرة. وآخرُها وصفةُ ملفٍ أضخمَ من أن يتّسع: عالجْ كلَّ سطرٍ ثم تقدم، دون أن تجمع الكلَّ أبدًا.
Pathlib: مساراتٌ بمفرداتٍ
قراءةُ دمجِ المساراتِ بـ + قراءةٌ أثريّة. يُسلّمك pathlib كائنَ Path وطرقُه تقول ما تفعل:
from pathlib import Path
p = Path("data") / "scores.txt" # Path('data/scores.txt')
text = p.read_text() # يقرأ الملف كله
lines = p.read_text().splitlines() # الأسطر بلا \n
p.exists() # True/False
p.is_file() # True/False
p.suffix # '.txt'
p.stem # 'scores'تربط الـ / الأجزاءَ في مسارٍ كما يربط نظامُ الملفاتِ المجلداتِ؛ وتستجوب exists وis_file وsuffix وstem ما هو المسار. تصير المساراتُ بياناتٍ بأجوبةٍ لا سلاسلَ تُشَرَّح.
الترميز: عقدُ الحروف
النصُّ بايتاتٌ حتى تُفسّرها اصطلاحٌ. ثبّت ذلك الاصطلاحَ لضمانِ قابليةِ الحمل بين الآلات:
with open("data.txt", encoding="utf-8") as f:
text = f.read()وبلا encoding، تسقط بايثون إلى افتراضِ النظامِ، الذي يختلف حسب المنصة ، الملفُ نفسُه مشوَّشٌ على آلةٍ ويندوزَ ونقيٌّ على لينكس. وذكرُ utf-8 يجعل البايتاتِ تعني الحروفَ نفسها في كل مكانٍ.
مثالٌ محلول: ملفُّ الدرجاتِ سطرًا سطرًا
المشيُ الآمنُ في الذاكرةِ ، تجميعٌ دونَ أن تحملَ الملفَّ كلَّهُ:
with open("scores.txt", encoding="utf-8") as f:
total = 0
count = 0
for line in f:
total += int(line.strip())
count += 1
print(f"Avg: {total / count}")تُقرأ كلُّ سطرٍ ويُقشَرُ سطرُه الجديدُ ويُحوَّلُ ويُفلَتُ قبلَ وصولِ التالي ، يتدفقُ الملفُّ دونَ أن يتجمعَ كلَّهُ. ويعدُ with بإغلاقِ الملفِّ حين تنتهي الكتلةُ، عاديًّا أو باستثناءٍ.
أخطاء شائعة
- نسيان
with. تتسرّب المعالجاتُ حين لا يغلقها شيءٌ؛ دع الكتلةَ تملك حياةَ الملف. - ابتلاعُ ملفاتٍ هائلة. قد يُنهك
f.read()ذاكرةً أمام ملفٍ عملاقٍ ، كرّرْ بـfor line in fبدلًا منه. - تجاهلُ الترميز. تتحوّل الحروفُ غير اللاتينيةِ إلى رموزٍ مبهمةٍ حين يترك الاصطلاحُ للصدفة.
- المساراتُ المقحَمة. يجعل
pathlib.Pathالكودَ نفسَه يتمشّى على كل نظامِ تشغيلٍ. - ملفٌّ مُستهلكٌ يُقرأ فارغًا. بعد
f.read()تستقرُّ الموضعُ في النهاية؛ تُعيدُ قراءةٌ ثانيةٌ''ويُعيدُreadlines()القائمةَ الفارغةَ. اقرأ مرةً، أو أعدِ الفتحَ.
🧩 تحديات
🧩 تحدٍّ ، فكّر أولًا ثم أظهر
عُدّ أسطرَ ملفٍ دون تحميله في الذاكرة.
💡 الجواب: count = 0; with open("file.txt") as f: for line in f: count += 1 أو المقتضبُ sum(1 for _ in open("file.txt")) ، سطرٌ واحدٌ كلَّ مرةٍ، لا الكلُّ أبدًا.
🧩 تحدٍّ ، فكّر أولًا ثم أظهر
اسرد كلَّ ملفٍ ذي لاحقةِ .txt في مجلدٍ باستخدامِ pathlib.
💡 الجواب: list(Path(".").glob("*.txt")) ، مستقبلُ باسمٍ عامٍّ يجوب لك الأسماءَ المطابقةَ.
🤔 أسئلة سقراطية
- هل يتضمّن
for line in fالـ\nالختاميَّ؟ ولماذا تبدو الحلقةُ بهذه الهيئة ، وكيف تُزيل السطرَ الجديدَ؟ - ماذا يحدثُ عند قراءةِ ملفٍ غيرِ موجودٍ؟ كيف يُصاول
withالاستثناءَ؟ - متى يسبق
f.read()التكرارَ سطرًا بسطرٍ؟
✅ فحص سريع
١. ماذا يفعل line.rstrip() في حلقةِ ملفٍ؟
٢. ما الطريقةُ الصحيحةُ لقراءةِ ملفٍ؟