Kodax

File I/O المتقدم — Python المتقدم

الملفات المتقدمة: pathlib والملفات الكبيرة والمؤقتة

تعلمت الأساسيات في دورة هياكل البيانات (open و with و JSON و CSV). هنا نركز على ما تحتاجه في الأدوات والسكربتات الحقيقية.

pathlib بدل نصوص المسارات

from pathlib import Path
base = Path("data")
report = base / "2026" / "report.txt"   # / تجمع المسارات بشكل صحيح على كل الأنظمة
report.parent.mkdir(parents=True, exist_ok=True)

| العملية | pathlib |

|---|---|

| قراءة/كتابة نص | read_text(encoding=...) / write_text(...) |

| وجود | exists()، is_file()، is_dir() |

| أجزاء الاسم | name، stem، suffix، parent |

| البحث | glob("*.csv")، rglob("*.py") (يشمل المجلدات الفرعية) |

| حذف | unlink() للملف، rmdir() لمجلد فارغ |

لا تجمع المسارات بـ + مثل "data" + "/" + name: الفاصل يختلف بين Windows (\\) وبقية الأنظمة.

الترميز دائماً

حدّد encoding="utf-8" في كل قراءة وكتابة نصية؛ الترميز الافتراضي يختلف بين الأجهزة، وفي Windows قد يفشل حفظ النص العربي.

الملفات الكبيرة

- نصية: المرور سطراً سطراً for line in f يقرأ سطراً واحداً في الذاكرة.

- ثنائية: القراءة بكتل ثابتة مع المولّدات:

def read_in_chunks(path, size=1024 * 1024):
    with open(path, "rb") as f:
        while chunk := f.read(size):
            yield chunk

:= (عامل الإسناد في التعبير) يقرأ الكتلة ويختبرها في نفس السطر، والحلقة تتوقف عند نهاية الملف (كتلة فارغة).

الملفات والمجلدات المؤقتة

tempfile.TemporaryDirectory() و NamedTemporaryFile() تنشئ أسماء فريدة في مجلد النظام المؤقت. مع with تُحذف تلقائياً. مفيدة للاختبارات والمعالجة الوسيطة.

الكتابة الآمنة

إذا انقطع البرنامج أثناء كتابة ملف مهم، قد يبقى نصف مكتوب. الأسلوب الآمن: اكتب في ملف مؤقت بجانبه ثم استبدل الأصلي دفعة واحدة بـ Path.replace().

shutil للعمليات الكبيرة

shutil.copy، shutil.move، shutil.rmtree (حذف مجلد كامل: احذر!)، shutil.make_archive لإنشاء ملف zip.

تمرين

اكتب سكربت يمر على كل ملفات .txt في مجلد ومجلداته الفرعية (rglob)، ويطبع عدد أسطر كل ملف وحجمه، ويحفظ النتيجة في summary.csv بكتابة آمنة.

import csv
import json
import os
import tempfile
from pathlib import Path

# ===== الأساسيات مع with =====
with open("file.txt", "w", encoding="utf-8") as f:
    f.write("مرحباً\n")
    f.writelines(["سطر 1\n", "سطر 2\n"])

with open("file.txt", encoding="utf-8") as f:
    for line in f:
        print(line.strip())

# ===== Path (الطريقة الحديثة) =====
folder = Path("data") / "output"
folder.mkdir(parents=True, exist_ok=True)
file_path = folder / "results.txt"
file_path.write_text("بيانات", encoding="utf-8")
print(file_path.read_text(encoding="utf-8"))
print(file_path.exists(), file_path.is_file(), file_path.suffix, file_path.stem)

for f in Path(".").glob("*.txt"):
    print("ملف نصي:", f.name)

# ===== JSON =====
data = {"name": "أحمد", "age": 25, "scores": [90, 85, 92]}
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)
with open("data.json", encoding="utf-8") as f:
    print(json.load(f))

# ===== CSV =====
with open("data.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["name", "age"])
    writer.writeheader()
    writer.writerow({"name": "أحمد", "age": 25})
    writer.writerow({"name": "محمد", "age": 30})
with open("data.csv", newline="", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        print(row["name"], row["age"])

# ===== الملفات الكبيرة: سطراً سطراً أو بكتل =====
def read_in_chunks(path, chunk_size=1024 * 1024):
    with open(path, "rb") as f:
        while chunk := f.read(chunk_size):
            yield chunk

print("حجم file.txt بالبايت:", sum(len(c) for c in read_in_chunks("file.txt")))

# ===== الملفات المؤقتة =====
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) as f:
    f.write("بيانات مؤقتة")
    temp_path = f.name
print("الملف المؤقت:", temp_path)
os.unlink(temp_path)

# تنظيف ملفات المثال
for name in ["file.txt", "data.json", "data.csv"]:
    os.remove(name)
file_path.unlink()
folder.rmdir()
folder.parent.rmdir()
اختبر فهمك