محتويات الدورة

C / المتوسط: الذاكرة والمكونات الموثوقة / الدرس 10

الملفات والدفقات ومعالجة الأخطاء

عامل الملف كمورد قابل للفشل واحفظ الفرق بين نهاية الإدخال والبيانات غير الصالحة وفشل الإدخال والإخراج.

وقت القراءة التقريبي: 5 دقيقةتاريخ مراجعة المحتوى
في هذه الصفحة

أهداف الدرس

  • فحص الفتح والنقل والإغلاق.
  • تمييز EOF من بايت مخزن ومن الخطأ.
  • تحديد حدود النص والثنائي والحفظ الدائم.

الفتح يحدد عقد الاستخدام

تعيد fopen مؤشر FILE عند النجاح وصفريا عند الفشل. لوضع الفتح أثر مهم: تقرأ "r" ملفا موجودا، وتنشئ "w" أو تقتطع، وتلحق "a" وفق عقد المكتبة. أضف "b" للوضع الثنائي عند الحاجة إلى منع تحويل النص. لا تعرض أوضاعا مدمرة على مسار مستخدم مجهول.

افصل اختيار المسار عن التحليل ومنطق التطبيق. يفيد دفق tmpfile المؤقت للأمثلة المعزولة لأنه يتجنب الكتابة فوق ملف موجود ويُزال عند الإغلاق. يحتاج التطبيق الحقيقي سياسة موثقة للإنشاء والاستبدال والأذونات والفشل؛ ولا تحدد ISO C وحدها كل سلوك أمان نظام التشغيل.

قراءة المحارف في int

تعيد fgetc قيمة unsigned char محولة إلى int أو EOF. خزن النتيجة في int حتى تبقى العلامة متميزة من بيانات المحارف الصحيحة. افحص الإرجاع قبل التضييق إلى بايت. تبلغ feof عن حالة نهاية حدثت بالفعل؛ ولا تتنبأ بنجاح القراءة التالية.

تقرأ الحلقة التالية دفقا معروفا من ثلاثة محارف ولا تزيد العدد إلا بعد قراءة ناجحة. وتفحص ferror بعد الحلقة لتمييز نهاية الإدخال المعتادة من خطأ الإدخال والإخراج. يستخدم المثال rewind بعد الكتابة لإقامة تغيير الاتجاه المطلوب في دفق تحديث قبل القراءة.

c عد قراءات المحارف الناجحة
#include <stdio.h>

int main(void)
{
    FILE *file = tmpfile();
    if (file == NULL) return 1;
    if (fputs("abc", file) == EOF) { fclose(file); return 1; }
    rewind(file);
    size_t count = 0;
    int ch;
    while ((ch = fgetc(file)) != EOF) ++count;
    if (ferror(file)) { fclose(file); return 1; }
    printf("characters=%zu\n", count);
    return fclose(file) == 0 ? 0 : 1;
}
الناتج المتوقع
characters=3

تعد الحلقة a وb وc؛ ولا تعد EOF نفسها محرفا أبدا.

أعداد النقل جزء من النتيجة

تعيد fread وfwrite عدد العناصر الكاملة المنقولة، وليس قيمة منطقية بسيطة. يجعل حجم العنصر واحدا النتيجة عدد بايتات ويسهل تحليل النقل الجزئي. قد تعني القراءة القصيرة نهاية الملف أو الخطأ؛ فافحص مؤشرات الدفق وفسرها ضمن سياق التنسيق.

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

c فحص رحلة بايتات محدودة ذهابا وإيابا
#include <stdio.h>
#include <string.h>

int main(void)
{
    const unsigned char source[] = {1, 2, 3, 4};
    unsigned char copy[4];
    FILE *file = tmpfile();
    if (file == NULL) return 1;
    if (fwrite(source, 1, sizeof source, file) != sizeof source) { fclose(file); return 1; }
    if (fseek(file, 0, SEEK_SET) != 0) { fclose(file); return 1; }
    if (fread(copy, 1, sizeof copy, file) != sizeof copy) { fclose(file); return 1; }
    puts(memcmp(source, copy, sizeof copy) == 0 ? "same bytes" : "different bytes");
    return fclose(file) == 0 ? 0 : 1;
}
الناتج المتوقع
same bytes

تظل البايتات الأربعة المهيأة محفوظة بعد الكتابة والقراءة عبر الدفق المؤقت.

حفظ معلومات الفشل المفيدة

عندما تفشل عملية، احفظ السياق المناسب قبل أن يغير التنظيف الحالة التشخيصية. تستطيع perror شرح errno إذا نص عقد العملية الفاشلة على استخدامها؛ ولا تفترض أن كل دالة C قياسية تضبطها لكل فشل. أبلغ العملية وموضع السجل الاصطناعي بجانب الرسالة المنخفضة المستوى عند الحاجة.

قد لا يظهر فشل كتابة إلا عند التفريغ أو الإغلاق لاحقا بسبب التخزين المؤقت. افحص fclose عندما يهم نجاح التسليم. تفرغ fflush(NULL) دفقات الإخراج، لكن fflush(stdin) ليست عملية محمولة في C17 للتخلص من الإدخال. استهلك الإدخال غير المطلوب وفق سياسة سجل محدودة وصريحة.

إغلاق الدفق ليس برهانا على الديمومة

التخزين المؤقت في C القياسية والحفظ الدائم في نظام التشغيل طبقتان مختلفتان. لا تحدد fclose الناجحة وحدها حفظا يقاوم الانهيار على وسيط فعلي. تعتمد بروتوكولات الاستبدال الدائم على النظام ونظام الملفات وقد تحتاج مزامنة ومعالجة مجلدات خاصة بالمنصة. وثق هذه الافتراضات بدلا من عرضها كضمانات ISO C.

أبق التحقق من التنسيق مستقلا عن النقل. تستطيع الاختبارات عندها استخدام دفقات مؤقتة وسجلات قصيرة وبايتات اصطناعية معيبة عمدا دون ملفات إنتاج. تحقق من التنظيف بعد فشل التحليل بدقة فشل الفتح نفسها، فالدفق يظل موردا حيا حتى عند رفض بياناته.

الأخطاء الشائعة وإصلاحها

يُعالج المحرف الأخير مرتين.

لماذا يحدث؟
استخدمت حلقة تتحكم فيها feof بيانات قديمة بعد فشل القراءة.
كيف تصلحه؟
اجعل قيمة إرجاع القراءة شرط الحلقة.
كيف تتجنب تكراره؟
اختبر الدفق الفارغ والدفق ذي المحرف الواحد.

يلتبس البايت 255 مع EOF.

لماذا يحدث؟
ضُيقت نتيجة fgetc إلى char قبل الفحص.
كيف تصلحه؟
خزن الإرجاع في int وقارنه مع EOF أولا.
كيف تتجنب تكراره؟
راجع واجهات العلامات للتأكد من نوع نتيجة مميز.

يُبلغ نجاح الحفظ رغم خطأ الإغلاق.

لماذا يحدث؟
فُحصت الكتابات المؤقتة السابقة وحدها.
كيف تصلحه؟
افحص النقل والتفريغ عند استخدامه ونتيجة fclose.
كيف تتجنب تكراره؟
حدد النجاح عند حد التسليم النهائي المطلوب.

اختبر فهمك

1. أعادت قراءة سجل من أربعة بايتات بايتين. اشرح التفسير الآمن وما يحظر فعله بعد ذلك.

اعرض الحل المشروح

عامل السجل كناقص. افحص ferror وfeof لتمييز فشل النقل من البتر المعتاد وأبلغ السياق المناسب. لا تفسر البايتين غير المقروءين في الوجهة ولا تقبل سجلا أقصر بصمت إذا اشترط التنسيق أربعة. أغلق الدفق في مسار الفشل واحفظ الحالة الصالحة السابقة للتطبيق.

2. لماذا لا يعالج فتح الدفق بالوضع الثنائي وحده كتابة struct خام؟

اعرض الحل المشروح

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

أهم ما تعلمته

  • تتحكم نتيجة القراءة في المعالجة؛ وتصف مؤشرات النهاية أحداثا مضت.
  • كل دفق حي يحتاج تنظيفا بما في ذلك رفض البيانات.
  • حدد هل يعني النجاح تسليم المكتبة أم التخزين الدائم الخاص بالمنصة.

المراجع الرسمية