مشكلة وحل 22: حل خطأ IsADirectoryError عند محاولة فتح مجلد كملف في بايثون

حل خطأ IsADirectoryError عند محاولة فتح مجلد كملف في بايثون

أثناء التعامل مع الملفات في بايثون، قد تكتب كودًا يبدو صحيحًا ثم يتوقف البرنامج برسالة مثل:

IsADirectoryError: [Errno 21] Is a directory: 'reports'

هذه الرسالة لا تعني أن مكتبة pathlib فيها مشكلة، ولا أن ترميز الملف خطأ. معناها المباشر أن الكود طلب تنفيذ عملية خاصة بملف، مثل القراءة أو الكتابة، لكنه أعطى بايثون مسار مجلد بدل مسار ملف.

{alertInfo} الفكرة ببساطة: استخدم is_file() عندما تريد قراءة ملف، واستخدم is_dir() عندما تريد التأكد أن المسار يشير إلى مجلد. وجود المسار وحده لا يكفي، لأن المسار قد يكون ملفًا أو مجلدًا.

{getToc} $title={محتوى المقال}

متى يظهر خطأ IsADirectoryError؟

يظهر الخطأ عندما تحاول استخدام مجلد كما لو كان ملفًا. أمثلة العمليات التي تتوقع ملفًا:

  • قراءة النص عبر open() أو read_text().
  • الكتابة داخل ملف عبر write_text() أو open(..., "w").
  • قراءة محتوى ملف CSV أو JSON من مسار يشير في الحقيقة إلى مجلد.

المثال التالي يعيد إنتاج المشكلة:

from pathlib import Path

path = Path("reports")

with path.open("r", encoding="utf-8") as file:
    content = file.read()

print(content)

إذا كان reports مجلدًا موجودًا، فإن path.open() لن يستطيع قراءته كنص، لأن هذه العملية تحتاج اسم ملف مثل reports/summary.txt.

project/ ├── reports/ ← مجلد │ └── summary.txt ← ملف └── app.py

في هذا التنظيم:

# خطأ: reports مجلد
Path("reports").read_text(encoding="utf-8")

# صحيح: summary.txt ملف داخل المجلد
file_path = Path("reports") / "summary.txt"
شرح الفرق بين المجلد والملف عند ظهور خطأ IsADirectoryError في بايثون

الخطأ الأكثر شيوعًا: الفحص بـ exists فقط

قد يحاول بعض المبتدئين حل المشكلة بهذا الشكل:

from pathlib import Path

path = Path("reports")

if path.exists():
    content = path.read_text(encoding="utf-8")

المشكلة أن exists() يجيب عن سؤال واحد فقط: هل يوجد هذا المسار؟ لكنه لا يخبرك هل هو ملف أم مجلد. لذلك قد تكون النتيجة True للمجلد reports، ثم يظهر الخطأ عند محاولة القراءة.

الحل هو فحص نوع المسار قبل محاولة قراءة محتواه:

from pathlib import Path

path = Path("reports")

if path.is_file():
    content = path.read_text(encoding="utf-8")
    print(content)
elif path.is_dir():
    print("المسار يشير إلى مجلد، وليس ملفًا.")
else:
    print("المسار غير موجود.")

الفرق بين exists وis_file وis_dir

الدالة السؤال الذي تجيب عنه مثال لنتيجة محتملة
exists() هل يوجد المسار أصلًا؟ قد تكون True للملف أو المجلد.
is_file() هل يشير المسار إلى ملف عادي؟ True لملف مثل notes.txt.
is_dir() هل يشير المسار إلى مجلد؟ True لمجلد مثل reports.

توفر مكتبة pathlib هذه الدوال للتعامل الواضح مع المسارات. راجع أيضًا درس شرح مكتبة pathlib للتعامل مع مسارات الملفات والمجلدات لتفهم طريقة بناء المسارات باستخدام العامل /.

فحص المسار باستخدام is_file وis_dir قبل قراءة الملفات في بايثون

الحل الصحيح عند معرفة اسم الملف

عندما تعرف أن الملف المطلوب موجود داخل مجلد محدد، ابنِ مساره كاملًا بدل فتح اسم المجلد:

from pathlib import Path

reports_folder = Path("reports")
file_path = reports_folder / "summary.txt"

if file_path.is_file():
    content = file_path.read_text(encoding="utf-8")
    print(content)
else:
    print("لم يتم العثور على الملف المطلوب.")

هذه الطريقة أفضل من كتابة مسار نصي طويل مثل "reports/summary.txt"، خصوصًا عندما تحتاج إلى بناء مسارات متعددة داخل مشروعك.

دالة آمنة لقراءة ملف نصي

في البرامج الحقيقية، لا تعتمد على الفحص فقط. قد يتغير المسار بين لحظة الفحص ولحظة القراءة، أو تحدث مشكلة في الصلاحيات. لذلك استخدم فحصًا واضحًا للمستخدم، ثم تعامل مع الأخطاء المحتملة بشكل محدد.

from pathlib import Path


def read_text_file(path_text):
    path = Path(path_text)

    if path.is_dir():
        print(f"لا يمكن قراءة المجلد كملف: {path}")
        return None

    if not path.is_file():
        print(f"الملف غير موجود: {path}")
        return None

    try:
        return path.read_text(encoding="utf-8")

    except PermissionError:
        print("لا توجد صلاحية كافية لقراءة هذا الملف.")

    except IsADirectoryError:
        print("المسار أصبح مجلدًا أو يشير إلى مجلد بدل ملف.")

    except UnicodeDecodeError:
        print("تعذر قراءة الملف بترميز UTF-8.")

    return None


content = read_text_file("reports/summary.txt")

if content is not None:
    print(content)

لاحظ أننا لم نستخدم except Exception أو except: بلا تحديد. التقاط الأخطاء المحددة يجعل الرسالة أوضح ولا يخفي أخطاء برمجية أخرى تحتاج إلى إصلاح.

{alertWarning} تنبيه: لا تستخدم is_file() وحدها كبديل كامل عن try/except. الفحص يقدّم رسالة مفهومة للمستخدم، لكن عملية القراءة نفسها يمكن أن تفشل لاحقًا بسبب الصلاحيات أو تغيير الملف أو المسار.

قراءة ملف نصي بطريقة آمنة مع التعامل مع IsADirectoryError في بايثون

مثال عملي: قراءة الملفات النصية من مجلد

أحيانًا يكون هدفك فعلًا التعامل مع مجلد، لكنك تريد قراءة الملفات الموجودة بداخله. في هذه الحالة لا تستدعِ read_text() على المجلد نفسه؛ اعرض العناصر، ثم اقرأ الملفات فقط.

reports/ ├── january.txt ├── february.txt ├── archive/ │ └── old-report.txt └── chart.png
from pathlib import Path

folder = Path("reports")

if not folder.is_dir():
    print("المجلد المطلوب غير موجود أو أن المسار ليس مجلدًا.")

else:
    for item in folder.iterdir():
        if item.is_file() and item.suffix.lower() == ".txt":
            content = item.read_text(encoding="utf-8")
            print(f"--- {item.name} ---")
            print(content)

الكود السابق يتجاهل المجلد archive والملف chart.png، لأنه يقرأ العناصر التي تحقق شرطين معًا:

  1. العنصر ملف حقيقي: item.is_file().
  2. امتداد الملف هو .txt.

تستطيع لاحقًا تطوير المثال لقراءة ملفات CSV أو JSON، لكن لا تفترض أن كل عنصر داخل المجلد مناسب للقراءة بالطريقة نفسها.

قراءة الملفات النصية فقط من مجلد باستخدام pathlib في بايثون

الفرق بين IsADirectoryError وأخطاء قريبة منه

الخطأ معناه مثال مختصر
IsADirectoryError استخدمت مجلدًا في عملية تحتاج ملفًا. Path("reports").read_text()
FileNotFoundError المسار أو الملف المطلوب غير موجود. Path("missing.txt").read_text()
PermissionError لا توجد صلاحية كافية للوصول إلى الملف أو المجلد. قراءة ملف محمي أو مفتوح بصلاحيات مقيدة.
NotADirectoryError استخدمت ملفًا في عملية تحتاج مجلدًا. محاولة استعراض ما داخل ملف كما لو كان مجلدًا.

يمكنك الرجوع إلى حل خطأ FileNotFoundError عند فتح الملفات عندما لا يكون الملف موجودًا، وإلى حل خطأ PermissionError عند فتح الملفات عندما يكون المسار صحيحًا لكن المشكلة في الصلاحيات.

هل أستخدم os أم pathlib؟

يمكن حل المشكلة باستخدام os.path.isfile() وos.path.isdir() أيضًا، لكن pathlib مناسب جدًا للمبتدئ لأنه يجمع بناء المسار وفحصه وقراءته في كائن واحد.

import os

path = "reports"

if os.path.isfile(path):
    with open(path, "r", encoding="utf-8") as file:
        print(file.read())

elif os.path.isdir(path):
    print("المسار مجلد وليس ملفًا.")

لا توجد مشكلة في استخدام os، خاصة إن كان مشروعك قديمًا ويستخدمه بالفعل. لكن لا تخلط الطريقتين بلا سبب داخل المثال نفسه؛ اختر أسلوبًا واضحًا وثابتًا.

أخطاء شائعة يجب تجنبها

1. فتح اسم المجلد بدل اسم الملف

لا تكتب Path("reports").read_text() إن كنت تريد قراءة ملف داخل المجلد. اكتب مسار الملف كاملًا مثل Path("reports") / "summary.txt".

2. الاعتماد على exists فقط

وجود المسار لا يثبت أنه ملف. استخدم is_file() أو is_dir() حسب ما تحتاجه.

3. استخدام except فارغ

try:
    content = path.read_text()

except:
    print("حدث خطأ.")

هذا الأسلوب يخفي سبب المشكلة. اكتب استثناءات محددة، مثل IsADirectoryError أو FileNotFoundError أو PermissionError، ثم اعرض رسالة مناسبة.

4. قراءة كل ما داخل المجلد بلا فلترة

داخل المجلد قد توجد صور ومجلدات فرعية وملفات مختلفة. افحص is_file() وفلتر الامتداد الذي تتوقعه قبل القراءة.

روابط داخلية مفيدة من بايثون العرب

الخلاصة

  • يظهر IsADirectoryError عندما تطلب من بايثون تنفيذ عملية ملف على مسار يشير إلى مجلد.
  • exists() يثبت وجود المسار فقط، لكنه لا يحدد نوعه.
  • استخدم is_file() قبل قراءة ملف، وis_dir() قبل استعراض محتوى مجلد.
  • عند قراءة ملف داخل مجلد، ابنِ مسار الملف كاملًا باستخدام Path("folder") / "file.txt".
  • تعامل مع الأخطاء المحددة داخل try/except بدل التقاط كل الأخطاء برسالة عامة.
  • عند استعراض مجلد، اقرأ الملفات المطابقة فقط واترك المجلدات والامتدادات غير المناسبة.
{alertSuccess} قاعدة عملية: قبل تنفيذ أي قراءة أو كتابة، اسأل: هل أتعامل مع ملف أم مجلد؟ هذا السؤال البسيط يمنع كثيرًا من أخطاء المسارات في مشاريع بايثون.

مصادر رسمية للتوسع

أسئلة شائعة

هل IsADirectoryError يعني أن المجلد غير موجود؟

لا. غالبًا يعني العكس: المسار موجود، لكنه مجلد بينما الكود كان يحتاج ملفًا. عندما لا يكون المسار موجودًا غالبًا ستواجه FileNotFoundError.

هل يكفي استخدام exists قبل read_text؟

لا. exists() لا يفرق بين الملف والمجلد. استخدم is_file() إذا كنت ستقرأ ملفًا، ثم تعامل مع الأخطاء المتوقعة عبر try/except.

كيف أقرأ ملفًا داخل مجلد؟

أنشئ مسارًا للملف نفسه، مثل Path("reports") / "summary.txt"، ثم تحقق من is_file() واقرأه باستخدام read_text() أو open().

هل أستخدم pathlib أم os لحل الخطأ؟

كلتاهما صالحتان. pathlib يقدم صياغة مرتبة لبناء المسارات وفحصها، بينما os.path مفيد في المشاريع التي تستخدمه مسبقًا. الأهم هو فحص ما إذا كان المسار ملفًا أو مجلدًا قبل العملية المناسبة.

لماذا أضع try وexcept بعد is_file؟

لأن الفحص يساعدك على إعطاء رسالة واضحة مبكرًا، لكنه لا يضمن أن القراءة ستنجح دائمًا. يمكن أن تتغير الصلاحيات أو حالة المسار قبل القراءة، لذلك يبقى التعامل المحدد مع الاستثناءات ضروريًا.

إرسال تعليق

أحدث أقدم