تخطَّ إلى المحتوى

التعامل مع الملفات في C: fopen و fprintf و fgets

الدرس 10 من 10 · C

كل ما خزنته في المتغيرات حتى الآن يختفي لحظة انتهاء البرنامج. لكن البرامج الحقيقية تتذكر: اللعبة تحفظ نتيجتك، والمحرر يحفظ نصك. السر هو الملفات (Files): بيانات تُكتب على القرص وتبقى بعد إغلاق البرنامج. في هذا الدرس ستتعلم كيف تكتب في ملف نصي وتقرأ منه، وكيف تتعامل مع أخطاء الملفات.

كل الدوال هنا من المكتبة stdio.h التي تستخدمها منذ الدرس الأول.

التعامل مع أي ملف يمر بثلاث خطوات، مثل دفتر على الرف: تفتحه، تكتب فيه أو تقرأ منه، ثم تغلقه وتعيده.

#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 بجانب برنامجك، وبداخله السطران.

المعامل الثاني في 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 C
2: Score: 95
  • fgets تقرأ سطراً واحداً في كل استدعاء، وتُرجع NULL عندما ينتهي الملف. ولهذا وضعناها شرطاً للحلقة.
  • السطر المقروء ينتهي برمز السطر الجديد \n، ولذلك لم نكتب \n في printf.
#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 C
Score: 95
Level: 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 -> 91
Omar -> 78
Laila -> 85
المتوسط: 84.7
  • fscanf تُرجع عدد القيم التي نجحت في قراءتها. ما دامت تُرجع 2 فقد قرأنا اسماً ودرجة، وعند نهاية الملف يتغير العدد وتتوقف الحلقة.
  • %29s: اقرأ نصاً بحد أقصى 29 حرفاً، حتى لا يتجاوز حجم المصفوفة name[30].

أهم قاعدة في الملفات: افحص ناتج 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 يحمي المصفوفة من ملف فيه أسطر أكثر مما تتسع له.

  1. اكتب برنامجاً يطلب من المستخدم ثلاثة أسماء ويحفظها في ملف names.txt، كل اسم في سطر.
  2. اكتب برنامجاً ثانياً يقرأ الملف ويطبع الأسماء مرقمة، مع عدد الأسطر في النهاية.
  3. اجعل البرنامج الأول يضيف إلى الملف بدل مسحه في كل تشغيل.
  4. تحدٍّ: اكتب عدّاد تشغيل: برنامج يقرأ رقماً من ملف counter.txt (أو يبدأ من صفر إن لم يوجد)، ويزيده واحداً، ويحفظه، ويطبع «هذا التشغيل رقم N».
  • عدم فحص NULL: استخدام مؤشر الملف دون التأكد من نجاح fopen أشهر سبب لانهيار برامج الملفات.
  • نسيان fclose: قد لا يُكتب آخر جزء من البيانات على القرص، ولكل برنامج حد أقصى من الملفات المفتوحة.
  • الوضع w بدل a: تفقد كل ما حفظته سابقاً دون أي تحذير.
  • مسار خاطئ: اسم الملف وحده يعني «في مجلد التشغيل الحالي»، وهو ليس بالضرورة مجلد ملف الكود. إذا لم يجد البرنامج الملف فتأكد من أين تشغّله.

سؤال

ماذا يحدث عند فتح ملف موجود بالوضع w؟

سؤال

ماذا تُرجع fopen إذا فشل فتح الملف؟

  • الخطوات ثابتة: fopen ثم القراءة أو الكتابة ثم fclose.
  • الوضع "r" للقراءة، و "w" للكتابة مع مسح القديم، و "a" للإضافة.
  • fprintf تكتب نصاً منسقاً، و fgets تقرأ سطراً، و fscanf تقرأ قيماً منسقة.
  • افحص NULL بعد كل fopen، وأغلق كل ملف فتحته.
  • مبروك! أنهيت كورس C. صرت تفهم الذاكرة والمؤشرات والملفات، وهي أساس كل لغة ستتعلمها بعدها.