التعامل مع الملفات في C: fopen و fprintf و fgets
كل ما خزنته في المتغيرات حتى الآن يختفي لحظة انتهاء البرنامج. لكن البرامج الحقيقية تتذكر: اللعبة تحفظ نتيجتك، والمحرر يحفظ نصك. السر هو الملفات (Files): بيانات تُكتب على القرص وتبقى بعد إغلاق البرنامج. في هذا الدرس ستتعلم كيف تكتب في ملف نصي وتقرأ منه، وكيف تتعامل مع أخطاء الملفات.
كل الدوال هنا من المكتبة stdio.h التي تستخدمها منذ الدرس الأول.
ثلاث خطوات دائماً
Section titled “ثلاث خطوات دائماً”التعامل مع أي ملف يمر بثلاث خطوات، مثل دفتر على الرف: تفتحه، تكتب فيه أو تقرأ منه، ثم تغلقه وتعيده.
#include <stdio.h>
int main(void) { FILE *file = fopen("notes.txt", "w"); if (file == NULL) { printf("تعذّر فتح الملف\n"); return 1; }
fprintf(file, "Hello from C\n"); fprintf(file, "Score: %d\n", 95);
fclose(file); printf("تم الحفظ\n"); return 0;}تم الحفظFILE *file: مؤشر إلى نوع خاص يمثل الملف المفتوح. كل دوال الملفات تتعامل معه.fopen("notes.txt", "w"): افتح الملف بوضع الكتابة. تُرجعNULLإذا فشل الفتح.fprintf(file, ...): مثلprintfتماماً، لكنها تكتب في الملف بدل الشاشة.fclose(file): أغلق الملف. الإغلاق يضمن وصول كل ما كتبته إلى القرص فعلاً.
بعد التشغيل ستجد ملفاً اسمه notes.txt بجانب برنامجك، وبداخله السطران.
أوضاع الفتح
Section titled “أوضاع الفتح”المعامل الثاني في fopen يحدد ماذا ستفعل بالملف:
| الوضع | المعنى | إذا كان الملف موجوداً | إذا لم يكن موجوداً |
|---|---|---|---|
"r" |
قراءة | يُفتح للقراءة | يفشل الفتح (NULL) |
"w" |
كتابة | يُمسح محتواه كله | يُنشأ ملف جديد |
"a" |
إضافة | تُضاف الكتابة في آخره | يُنشأ ملف جديد |
القراءة سطراً سطراً: fgets
Section titled “القراءة سطراً سطراً: fgets”تعرفت على fgets في درس النصوص لقراءة ما يكتبه المستخدم. هي نفسها تقرأ من الملفات: فقط أعطها مؤشر الملف بدل stdin.
#include <stdio.h>
int main(void) { FILE *file = fopen("notes.txt", "r"); if (file == NULL) { printf("تعذّر فتح الملف\n"); return 1; }
char line[100]; int count = 0; while (fgets(line, sizeof(line), file) != NULL) { count++; printf("%d: %s", count, line); }
fclose(file); return 0;}1: Hello from C2: Score: 95fgetsتقرأ سطراً واحداً في كل استدعاء، وتُرجعNULLعندما ينتهي الملف. ولهذا وضعناها شرطاً للحلقة.- السطر المقروء ينتهي برمز السطر الجديد
\n، ولذلك لم نكتب\nفيprintf.
الإضافة: الوضع a
Section titled “الإضافة: الوضع a”#include <stdio.h>
int main(void) { FILE *file = fopen("notes.txt", "a"); if (file == NULL) { return 1; } fprintf(file, "Level: %d\n", 3); fclose(file);
file = fopen("notes.txt", "r"); if (file == NULL) { return 1; } char line[100]; while (fgets(line, sizeof(line), file) != NULL) { printf("%s", line); } fclose(file); return 0;}Hello from CScore: 95Level: 3السطران القديمان بقيا، والسطر الجديد أُضيف في النهاية. بهذا الوضع تُكتب ملفات السجلات (Logs) التي تسجل ما يحدث في البرنامج سطراً بعد سطر.
قراءة بيانات منسقة: fscanf
Section titled “قراءة بيانات منسقة: fscanf”إذا كان الملف يحتوي بيانات مرتبة، مثل اسم ثم درجة في كل سطر، فالدالة fscanf تقرأها مباشرة في متغيرات، كما تفعل scanf مع لوحة المفاتيح:
#include <stdio.h>
int main(void) { FILE *out = fopen("scores.txt", "w"); if (out == NULL) { return 1; } fprintf(out, "Sara 91\nOmar 78\nLaila 85\n"); fclose(out);
FILE *in = fopen("scores.txt", "r"); if (in == NULL) { return 1; }
char name[30]; int score; int total = 0; int count = 0; while (fscanf(in, "%29s %d", name, &score) == 2) { printf("%s -> %d\n", name, score); total += score; count++; } fclose(in);
printf("المتوسط: %.1f\n", (float)total / count); return 0;}Sara -> 91Omar -> 78Laila -> 85المتوسط: 84.7fscanfتُرجع عدد القيم التي نجحت في قراءتها. ما دامت تُرجع 2 فقد قرأنا اسماً ودرجة، وعند نهاية الملف يتغير العدد وتتوقف الحلقة.%29s: اقرأ نصاً بحد أقصى 29 حرفاً، حتى لا يتجاوز حجم المصفوفةname[30].
الملف غير موجود
Section titled “الملف غير موجود”أهم قاعدة في الملفات: افحص ناتج fopen دائماً. الملف قد يكون محذوفاً، أو في مجلد آخر، أو بلا صلاحية قراءة.
#include <stdio.h>
int main(void) { FILE *file = fopen("missing.txt", "r"); if (file == NULL) { printf("الملف غير موجود، سنبدأ ببيانات فارغة\n"); return 0; } fclose(file); return 0;}الملف غير موجود، سنبدأ ببيانات فارغةلو تجاهلت الفحص ومررت NULL إلى fgets أو fprintf لانهار البرنامج. لاحظ أن غياب الملف ليس دائماً خطأ: برنامج يُشغَّل لأول مرة لن يجد ملف الحفظ، وهذا طبيعي.
مثال متكامل: حفظ هياكل وتحميلها
Section titled “مثال متكامل: حفظ هياكل وتحميلها”لنجمع ما تعلمته في الدروس الأخيرة: مصفوفة هياكل تُحفظ في ملف ثم تُقرأ منه.
#include <stdio.h>
typedef struct { char name[30]; int price;} Product;
int main(void) { Product items[3] = {{"Pen", 5}, {"Notebook", 15}, {"Ruler", 7}};
FILE *out = fopen("products.txt", "w"); if (out == NULL) { return 1; } for (int i = 0; i < 3; i++) { fprintf(out, "%s %d\n", items[i].name, items[i].price); } fclose(out);
Product loaded[10]; int count = 0; FILE *in = fopen("products.txt", "r"); if (in == NULL) { return 1; } while (count < 10 && fscanf(in, "%29s %d", loaded[count].name, &loaded[count].price) == 2) { count++; } fclose(in);
printf("تم تحميل %d منتجات\n", count); printf("الأخير: %s بسعر %d\n", loaded[count - 1].name, loaded[count - 1].price); return 0;}تم تحميل 3 منتجاتالأخير: Ruler بسعر 7الشرط count < 10 يحمي المصفوفة من ملف فيه أسطر أكثر مما تتسع له.
جرّب بنفسك
Section titled “جرّب بنفسك”- اكتب برنامجاً يطلب من المستخدم ثلاثة أسماء ويحفظها في ملف
names.txt، كل اسم في سطر. - اكتب برنامجاً ثانياً يقرأ الملف ويطبع الأسماء مرقمة، مع عدد الأسطر في النهاية.
- اجعل البرنامج الأول يضيف إلى الملف بدل مسحه في كل تشغيل.
- تحدٍّ: اكتب عدّاد تشغيل: برنامج يقرأ رقماً من ملف
counter.txt(أو يبدأ من صفر إن لم يوجد)، ويزيده واحداً، ويحفظه، ويطبع «هذا التشغيل رقم N».
أخطاء شائعة
Section titled “أخطاء شائعة”- عدم فحص NULL: استخدام مؤشر الملف دون التأكد من نجاح
fopenأشهر سبب لانهيار برامج الملفات. - نسيان fclose: قد لا يُكتب آخر جزء من البيانات على القرص، ولكل برنامج حد أقصى من الملفات المفتوحة.
- الوضع w بدل a: تفقد كل ما حفظته سابقاً دون أي تحذير.
- مسار خاطئ: اسم الملف وحده يعني «في مجلد التشغيل الحالي»، وهو ليس بالضرورة مجلد ملف الكود. إذا لم يجد البرنامج الملف فتأكد من أين تشغّله.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ماذا يحدث عند فتح ملف موجود بالوضع w؟
الوضع w يبدأ بملف فارغ دائماً. للإضافة على المحتوى الموجود استخدم الوضع a.
سؤال
ماذا تُرجع fopen إذا فشل فتح الملف؟
تُرجع المؤشر NULL، ويجب فحصه قبل أي قراءة أو كتابة.
الخلاصة
Section titled “الخلاصة”- الخطوات ثابتة:
fopenثم القراءة أو الكتابة ثمfclose. - الوضع
"r"للقراءة، و"w"للكتابة مع مسح القديم، و"a"للإضافة. fprintfتكتب نصاً منسقاً، وfgetsتقرأ سطراً، وfscanfتقرأ قيماً منسقة.- افحص
NULLبعد كلfopen، وأغلق كل ملف فتحته. - مبروك! أنهيت كورس C. صرت تفهم الذاكرة والمؤشرات والملفات، وهي أساس كل لغة ستتعلمها بعدها.