عمليات الملفات في بايثون

عمليات الملفات في بايثون

توضح هذه المقالة عمليات الملفات في بايثون۔

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

YouTube Video

عمليات الملفات في بايثون

عمليات التعامل مع الملفات هي مهارة أساسية ضرورية، من السكريبتات الصغيرة إلى التطبيقات واسعة النطاق۔

فتح وإغلاق الملفات

أولاً، دعونا ننظر إلى أمثلة لقراءة وكتابة الملفات النصية۔ استخدام عبارة with (مدير السياق) يضمن إغلاق الملف بشكل صحيح۔

يفتح الكود التالي ملفًا نصيًا، ويقرأ محتواه، ويعالجه سطرًا سطرًا۔

1# Open and read a text file safely using a context manager.
2with open("example.txt", "r", encoding="utf-8") as f:
3    # Read all lines lazily (as an iterator) and process each line.
4    for line in f:
5        print(line.rstrip())
  • تحديد encoding="utf-8" صراحةً يساعد في تقليل المشاكل المتعلقة باختلافات الأنظمة۔
  • حتى مع الملفات الكبيرة، فإن for line in f يوفر كفاءة جيدة في استخدام الذاكرة۔

الكتابة (الإحلال والإلحاق)

انتبه لوضع الكتابة عند كتابة الملفات۔ w للإحلال، و a للإلحاق۔ استخدم عبارة with أيضًا عند الكتابة۔

الكود التالي يعرض أمثلة أساسية على الإحلال والإلحاق۔

1# Write (overwrite) to a file.
2with open("output.txt", "w", encoding="utf-8") as f:
3    f.write("First line\n")
4    f.write("Second line\n")
5
6# Append to the same file.
7with open("output.txt", "a", encoding="utf-8") as f:
8    f.write("Appended line\n")
  • يكتب هذا الكود نصًا إلى output.txt ثم يلحق المزيد من النص إلى نفس الملف۔ وضع "w" يقوم بالإحلال على الملف، بينما وضع "a" يلحق سطرًا جديدًا إلى نهاية المحتوى الحالي۔
  • إذا كنت بحاجة للتفريغ، استدعي flush()، لكن عادةً ما يتم التفريغ تلقائيًا عند انتهاء السياق۔
  • إذا كان هناك عمليات متعددة أو خيوط قد تكتب في نفس الوقت، يجب أن تأخذ في الاعتبار التحكم الحصري، مثل قفل الملف۔

قراءة وكتابة البيانات الثنائية

يتم التعامل مع الصور والملفات المضغوطة بوضع البيانات الثنائية (rb أو wb)۔ على عكس وضع النص، يتم تجاهل encoding۔

الكود التالي يقرأ ملفًا ثنائيًا وينسخه إلى ملف آخر بوضع البيانات الثنائية۔

1# Read and write binary files.
2with open("image.png", "rb") as src:
3    data = src.read()
4
5with open("copy.png", "wb") as dst:
6    dst.write(data)
  • عند التعامل مع ملفات ثنائية كبيرة، تجنب قراءتها دفعة واحدة باستخدام read()؛ القراءة والكتابة على دفعات أكثر كفاءة في الذاكرة۔

مثال للتعامل مع الملفات الكبيرة على دفعات

للملفات الضخمة التي لا تتسع في الذاكرة، اقرأها واكتبها على دفعات بحجم ثابت۔ نظرًا لأنها مرتبطة بعمليات الإدخال/الإخراج، فإن ضبط حجم المخزن المؤقت مفيد۔

الكود التالي ينسخ ملفًا على دفعات بحجم 64 كيلوبايت۔ يعمل هذا بسرعة مع الحفاظ على استهلاك منخفض للذاكرة۔

1# Copy a large file in chunks to avoid using too much memory.
2CHUNK_SIZE = 64 * 1024  # 64 KB
3
4with open("large_source.bin", "rb") as src,      open("large_dest.bin", "wb") as dst:
5    while True:
6        chunk = src.read(CHUNK_SIZE)
7        if not chunk:
8            break
9        dst.write(chunk)
  • يمكنك ضبط حجم الدفعة حسب خصائص الشبكة أو القرص لديك۔ في محركات SSD، غالبًا ما يكون حجم الدفعة الأكبر قليلاً أفضل أداءً۔

مثال على استخدام وسيطة التخزين المؤقت (buffering)

من خلال تحديد وسيطة buffering في دالة open()، يمكنك التحكم في حجم المخزن المؤقت المستخدم داخليًا بواسطة بايثون۔ هذا يتيح لك تحسين كفاءة الإدخال/الإخراج بشكل أكبر۔

 1# Explicitly set the internal buffer size to 128 KB for faster I/O.
 2CHUNK_SIZE = 64 * 1024
 3BUFFER_SIZE = 128 * 1024  # 128 KB internal buffer
 4
 5with open("large_source.bin", "rb", buffering=BUFFER_SIZE) as src,      open("large_dest.bin", "wb", buffering=BUFFER_SIZE) as dst:
 6    while True:
 7        chunk = src.read(CHUNK_SIZE)
 8        if not chunk:
 9            break
10        dst.write(chunk)
  • إذا قمت بتعيين قيمة buffering إلى 0، فسيتم تنفيذ الإدخال/الإخراج بدون تخزين مؤقت؛ القيمة 1 تُمكن التخزين المؤقت على مستوى السطر، في حين أن القيم 2 أو أعلى تستخدم مخزنًا مؤقتًا بالحجم المحدد بالبايتات۔
  • بشكل عام، تكون القيمة الافتراضية كافية لأن نظام التشغيل يتعامل بكفاءة مع التخزين المؤقت، لكن ضبط هذا المعامل يمكن أن يكون فعالًا مع الملفات الكبيرة جدًا أو الأجهزة الخاصة۔

عمليات الملفات الحديثة باستخدام Pathlib

يجعل الموديول القياسي pathlib إدارة المسارات أكثر سهولة ووضوحًا۔ يحسن من سهولة القراءة والسلامة مقارنةً بمسارات الأحرف النصية۔

 1from pathlib import Path
 2
 3path = Path("data") / "notes.txt"
 4
 5# Ensure parent directory exists.
 6path.parent.mkdir(parents=True, exist_ok=True)
 7
 8# Write and read using pathlib.
 9path.write_text("Example content\n", encoding="utf-8")
10content = path.read_text(encoding="utf-8")
11print(content)
  • يعرض هذا الكود كيفية إنشاء مجلد باستخدام pathlib وكتابة ملف نصي وقراءة محتواه۔ مع كائن Path، يمكنك معالجة المسارات بسهولة وأمان۔
  • يوفر الكائن Path واجهات برمجة تطبيقية مريحة مثل iterdir() و glob()۔ يمكنك كتابة كود دون القلق من فواصل المسارات بين أنظمة التشغيل المختلفة۔

الملفات والمجلدات المؤقتة (tempfile)

يمكن إنشاء الملفات المؤقتة بأمان باستخدام tempfile۔ هذا يتجنب تعارض الأسماء ومشاكل تنافس العمليات الأمنية۔

الكود التالي يعرض مثالاً لإنشاء بيانات مؤقتة باستخدام ملف مؤقت۔

 1import tempfile
 2
 3# Create a temporary file that is deleted on close.
 4with tempfile.NamedTemporaryFile(
 5    mode="w+",
 6    encoding="utf-8",
 7    delete=True
 8) as tmp:
 9    tmp.write("temporary data\n")
10    tmp.seek(0)
11    print(tmp.read())
12
13# tmp is deleted here
  • ينشئ هذا الكود ملفًا مؤقتًا، يكتب ويقرأ البيانات، ويحذفه تلقائيًا عند انتهاء كتلة with۔ باستخدام tempfile.NamedTemporaryFile، يمكنك التعامل مع الملفات المؤقتة بأمان ودون تعارض۔ نظرًا لتحديد delete=True، يتم حذف الملف تلقائيًا۔
  • في ويندوز، قد لا تتمكن من فتح الملف من مقبض آخر فورًا، لذا يمكنك تعيين delete=False وإدارة الحذف بنفسك۔

shutil: عمليات عالية المستوى للنسخ والنقل والحذف

النسخ والنقل والحذف التكراري للملفات والمجلدات سهل باستخدام shutil۔

 1import shutil
 2
 3# Copy a file preserving metadata.
 4shutil.copy2("source.txt", "dest.txt")
 5
 6# Move a file or directory.
 7shutil.move("old_location", "new_location")
 8
 9# Remove an entire directory tree (use with caution).
10shutil.rmtree("some_directory")
  • shutil.copy2 ينسخ أيضًا البيانات الوصفية (مثل وقت التعديل)۔ move ستعود للانتقال العادي للملف حتى إذا لم يكن بإمكانها إعادة التسمية۔
  • rmtree عملية خطيرة، لذلك تحقق واحتفظ بنسخة احتياطية دائمًا قبل الحذف۔

البيانات الوصفية للملف (os.stat) والتعامل مع الأذونات

يمكن قراءة وتعديل حجم الملف ووقت التعديل والأذونات باستخدام os و stat۔

يحصل الكود التالي على معلومات أساسية عن الملف باستخدام os.stat ويغير الأذونات باستخدام os.chmod۔

 1import os
 2import stat
 3from datetime import datetime
 4
 5st = os.stat("example.txt")
 6print("size:", st.st_size)
 7print("modified:", datetime.fromtimestamp(st.st_mtime))
 8
 9# Make file read-only for owner.
10os.chmod("example.txt", stat.S_IREAD)
  • تعمل الأذونات بشكل مختلف على أنظمة POSIX وويندوز۔ إذا كانت توافقية التشغيل بين الأنظمة مهمة، استخدم واجهات عالية المستوى أو أضف معالجة شرطية۔

قفل الملف (التحكم الحصري) — الاختلافات بين يونكس وويندوز

التحكم الحصري مطلوب عندما تصل عمليات متعددة إلى نفس الملف في نفس الوقت۔ يستخدم يونكس fcntl، بينما يستخدم ويندوز msvcrt۔

الكود التالي يستخدم fcntl.flock على أنظمة يونكس للحصول على قفل حصري عند الكتابة۔

 1# Unix-style file locking example
 2import fcntl
 3
 4with open("output.txt", "a+", encoding="utf-8") as f:
 5    fcntl.flock(f, fcntl.LOCK_EX)
 6    try:
 7        f.write("Safe write\n")
 8        f.flush()
 9    finally:
10        fcntl.flock(f, fcntl.LOCK_UN)
  • يحصل هذا الكود على قفل حصري باستخدام fcntl.flock على أنظمة تشبه يونكس للكتابة بأمان ومنع الكتابة المتزامنة إلى الملف۔ يجب دائمًا تحرير القفل بعد المعالجة للسماح بوصول العمليات الأخرى۔
  • في ويندوز، استخدم msvcrt.locking()۔ للاستخدام الأكثر تقدمًا، فكر في استخدام مكتبات خارجية مثل portalocker أو filelock۔

أنماط الكتابة الذرية للملفات

لمنع تلف الملف أثناء التحديثات، اكتب إلى ملف مؤقت ثم استبدله باستخدام os.replace عند النجاح۔

الكتابة إلى ملف مؤقت ثم الاستبدال تساعد على تجنب التلف إذا حدث تعطل۔

 1import os
 2from pathlib import Path
 3import tempfile
 4
 5def atomic_write(path: Path, data: str, encoding="utf-8"):
 6    # Write to a temp file in the same directory and atomically replace.
 7    dirpath = path.parent
 8    with tempfile.NamedTemporaryFile(
 9        mode="w",
10        encoding=encoding,
11        dir=str(dirpath),
12        delete=False
13    ) as tmp:
14        tmp.write(data)
15        tmp.flush()
16        tempname = tmp.name
17
18    # Atomic replacement (on most OSes)
19    os.replace(tempname, str(path))
20
21# Usage
22atomic_write(Path("config.json"), '{"key":"value"}\n')

os.replace يجري عملية استبدال ذرية في نفس نظام الملفات۔ لاحظ أن الذرية لا تضمن عبر نقاط الربط المختلفة۔

الوصول السريع باستخدام mmap (للمعطيات الضخمة)

للوصول العشوائي للملفات الكبيرة، mmap يحسن أداء الإدخال/الإخراج۔ يتعلق ذلك بشكل أساسي بالعمليات الثنائية۔

خريطة الذاكرة في الكود التالي تعين ملفًا وتقرأ أو تكتب نطاقات معينة من البايتات۔ كن حذرًا عند تغيير حجم الملف۔

1import mmap
2
3with open("data.bin", "r+b") as f:
4    mm = mmap.mmap(f.fileno(), 0)  # map entire file
5    try:
6        print(mm[:20])  # first 20 bytes
7        mm[0:4] = b"\x00\x01\x02\x03"  # modify bytes
8    finally:
9        mm.close()
  • يعين هذا الكود ملفًا ثنائيًا إلى الذاكرة باستخدام mmap ويجري عمليات القراءة/الكتابة المباشرة على مستوى البايت۔ تتيح خريطة الذاكرة وصولًا عشوائيًا وسريعًا لمجموعات بيانات كبيرة۔
  • mmap فعال، ولكن الاستخدام غير الصحيح قد يؤدي إلى مشاكل في اتساق البيانات۔ استدعِ flush() للمزامنة عند الحاجة۔

CSV / JSON / Pickle: القراءة والكتابة حسب الصيغة

كل صيغة بيانات لها مكتبات مخصصة۔ استخدم csv لصيغة CSV، و json لـ JSON، و pickle لحفظ كائنات بايثون۔

الكود التالي يوضح أمثلة أساسية للقراءة والكتابة بصيغة CSV وJSON واستخدام Pickle۔ نظرًا لأن Pickle يمكنه تنفيذ تعليمات برمجية عشوائية، تجنب تحميل البيانات من مصادر غير موثوقة۔

 1import csv
 2import json
 3import pickle
 4
 5# CSV write
 6with open("rows.csv", "w", encoding="utf-8", newline="") as f:
 7    writer = csv.writer(f)
 8    writer.writerow(["name", "age"])
 9    writer.writerow(["Alice", 30])
10
11# JSON write
12data = {"items": [1, 2, 3]}
13with open("data.json", "w", encoding="utf-8") as f:
14    json.dump(data, f, ensure_ascii=False, indent=2)
15
16# Pickle write (only for trusted environments)
17obj = {"key": "value"}
18with open("obj.pkl", "wb") as f:
19    pickle.dump(obj, f)
  • يوصى بتحديد newline="" مع CSV لمنع ظهور أسطر فارغة إضافية في ويندوز۔ مع ensure_ascii=False، سيبقى JSON محتفظًا بالحروف UTF-8 المقروءة۔

القراءة والكتابة المباشرة للملفات المضغوطة (gzip / bz2 / zipfile)

التعامل المباشر مع gzip وzip يوفر مساحة على القرص۔ تتضمن المكتبة القياسية وحدات مدمجة مناسبة۔

الكود التالي هو مثال بسيط لقراءة وكتابة ملفات gzip كنص۔

1import gzip
2
3# Write gzipped text.
4with gzip.open("text.gz", "wt", encoding="utf-8") as f:
5    f.write("Compressed content\n")
6
7# Read gzipped text.
8with gzip.open("text.gz", "rt", encoding="utf-8") as f:
9    print(f.read())
  • هناك مقايضة بين نسبة الضغط والسرعة حسب مستوى الضغط والصيغة۔

إجراءات الأمان ومعالجة الثغرات

يمكن أخذ النقاط التالية في الاعتبار لإجراءات الأمان ومعالجة الثغرات۔

  • لا تستخدم إدخالاً غير موثوق به مباشرة في أسماء أو مسارات الملفات۔
  • استخدم Pickle فقط مع المصادر الموثوقة۔
  • قلل من امتيازات التنفيذ وامنح أقل الأذونات اللازمة للعمليات التي تتعامل مع الملفات۔
  • استخدم الملفات المؤقتة مع tempfile ولا تخزن الملفات العادية في المجلدات العامة۔

إذا كنت تستخدم مدخلات المستخدم في مسارات الملفات، فالتطبيع والتحقق ضروريان۔ على سبيل المثال، استخدم Path.resolve() وتحقق من المجلدات الرئيسية۔

 1from pathlib import Path
 2
 3def safe_path(base_dir: Path, user_path: str) -> Path:
 4    candidate = (base_dir / user_path).resolve()
 5    if base_dir.resolve() not in candidate.parents and base_dir.resolve() != candidate:
 6        raise ValueError("Invalid path")
 7    return candidate
 8
 9# Usage
10base = Path("/srv/app/data")
11
12# Raises an exception: attempted path traversal outside `base`
13safe = safe_path(base, '../etc/passwd')
  • كن حذرًا بشكل خاص عند استخدام مدخلات خارجية كمسارات ملفات في تطبيقات الويب أو واجهات برمجة التطبيقات العامة۔

ملخص الأنماط الشائعة

  • استخدم دائمًا عبارة with (إغلاق تلقائي)۔
  • حدد encoding صراحةً للبيانات النصية۔
  • اقرأ واكتب الملفات الكبيرة على دفعات۔
  • اعتمد قفل الملفات للموارد المشتركة۔
  • للتحديثات الهامة، استخدم النمط الذري 'الكتابة في ملف مؤقت → os.replace'۔
  • تأكد دائمًا وقم بإنشاء نسخ احتياطية قبل تنفيذ العمليات الخطيرة (مثل الحذف أو الكتابة فوق الملفات)۔
  • قم بتطبيع والتحقق عند استخدام إدخال خارجي كمسارات للملفات۔

الملخص

في العمليات على الملفات، من المهم استخدام تقنيات آمنة وموثوقة مثل بيان with، وتحديد الترميز بوضوح، والكتابة الذرية۔ في حالات المعالجة الواسعة أو الوصول المتوازي، من الضروري تنفيذ أنظمة القفل وإدارة السجلات لمنع تلف البيانات أو التعارضات۔ موازنة الكفاءة والأمان هو المفتاح لعمليات الملفات الموثوقة۔

يمكنك متابعة المقالة أعلاه باستخدام Visual Studio Code على قناتنا على YouTube.۔ يرجى التحقق من القناة على YouTube أيضًا.۔

YouTube Video