التعامل مع الملفات في لغة سي C
الملفات ( files ) في لغة سي, إن من مميزات لغة البرمجة القوية هو قدرتها على التعامل مع الملفات و لغة سي تُتيح لنا ذلك من خلال مكتبة stdio.h حيث نستطيع فتح الملفات و الكتابة فيها أو القراءة منها بإستخدام دوال الملفات. إن عملية الكتابة في ملف أو القراءة منه تدخل في ثلاث مراحل أساسية و هي:
- يتم إستخدام دالة fopen() لفتح الملف و من خلال هذه الدالة يُمكننا تحديد ما إن كنا نريد الكتابة في ملف. أو القراءة منه أو كلتا العمليتين معاً, كما يتم أيضاً تحديد ما إذ كنا نريد القراءة أو الكتابة يحتوي على نص txt file أو ملف ثنائي. binnary file مثل الصور و ملفات الفيديو و غيرها.
- يتم في هذه المرحلة القراءة من الملف عبر الدالة fread() أو الكتابة عليه عبر الدالة fwrite() و غيرها.
- يتم إغلاق الملف بإستخدام الدالة fclose().
إثناء قراءتنا من ملف أو كتابتنا فيه يمكننا التنقل بداخل الملف بحيث نستطيع قراءة جزء معين منه أو كتابة محتوى معين في مكان محدد من هذا الملف. و للقيام بذلك نسبق دالة القراءة fread() أو دالة الكتابة fwrite() بدالة اخرى تسمى fseek() بحيث نستطيع. بواسطة هذه الدالة تغيير مكاننا الحالي في الملف من خلال تحديد عدد البايتس المراد الإنتقال بها. و إن تهنا أو ضعنا في الملف في أي وقت و لم نعرف أين نحن في الملف بعد إستخدامنا لدالة fseek(). فيمكننا الرجوع بإستخدام دالة ftell() التي تقوم بإرجاع رقم من نوع on site integer يدلنا على مكاننا. في الملف بحسب عدد البايتس التي إجتزناها من بداية الملف.
في حالتنا التي في الصورة السابقة مثلاً إذا أردنا الانتقال إلى المكان 25 عند إشارة التعجب فيه عبر الأمر
fseek(f, 25, SEEK_SET);
و قمنا بعد ذلك بإستدعاء الدالة ftell() سوف تقوم بإرجاع لنا العدد 25 أي أننا نبعد عن بداية الملف 25 بايت. و بما أنه بنظام آسكي ASCII يكون حجم الحرف أو الرمز بايت واحد فقط, فذلك يعني أننا على بعد 25 رمزاً من بداية الملف. و لا ننسى ايضاً أن الفراغات و الأسطر الجديدة تُحسَب على أنها رموز و تأخذ حيزاً من الذاكرة كأي رمز أو أي حرف آخر.
المعاملات التي تستخدم مع الدالة fopen لغة سي
fopen("مسار الملف", "r") للقراءة
fopen("مسار الملف", "w") للكتابة بحال كان نظام آسكي
fopen("مسار الملف", "wb") للكتابة بحال كان نظام ثنائي كتابة البايث مباشرتاً
نستخدم binnary FILE بحال كنا نريد التعامل مع الصور jpg, png و الصوت مثل mp3, wave و غيرها, هذه الملفات نستخدم معها "wb".
الكتابة في ملف في لغة السي C
سوف نتعرف في هذه الفقرة على كيفية الكتابة في ملف و الفقرة التي تليها سوف نقوم بالقراءة من نفس الملف الذي كتبنا فيه. كما تعلًمنا سابقاً يجب فتح الملف بإستخدام fopen حتى نتمكن من التعامل معه سواء بالقراءة أو الكتابة و الدالة fopen تأخذ معاملين و هما :
- نص يمثل مسار الملف في القرص الصلب من جهاز الكمبيوتر. و إن كان الملف غير موجود فسوف يتم إنشاءه بشكل تلقائي من قبل الدالة fopen.
- نص يمثل نوع العملية المراد إجراءها على الملف, مثلاً يمثل الرمز "w" عملية. الكتابة و "r" عملية القراءة و أخيراً تقوم الدالة بإرجاع مؤشر من نوع FILE.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *f = fopen("/home/data/Desktop/SJO.txt", "w");
char *s = "Nusur Tech MOHAMMED AND SAJA";
fwrite(s, sizeof(char), strlen(s), f);
fclose(f);
return 0;
}
لقد قُمنا بإنشاء مؤشر pointer من نوع FILE و نتأكد هنا أننا كتبناها باﻷحرف الكبيرة Capital و المؤشر هو f و كتبنا. الدالة fopen من أجل فتح الملف الموجود في جهاز الكمبيوتر. وحددنا مساره أنه موجود في سطح المكتب و إسمه DB ثم حددنا نوع العملية و هي الكتابة عن طريق كتابة "w". و بعد ذلك قُمنا بتعريف النص في هذا الملف النصي عن طريق char من نوع pointer و هو s ثم. كتبنا النص و هو Nusur Tech هذا هو النص الذي سيتم كتابته بداخل الملف النصي. بعدها قُمنا بكتابة الدالة fwrite من أجل كتابة النص السابق في الملف الذي فتحناه في السطر السابق. و الدالة fwrite تأخذ النص المُراد كتابتهُ كمعامل أول ووضعنا لها s لأنه يحمل النص السابق.
و المعامل الثاني في هذه الدالة هو حجم كل نص يتم كتابته في الملف, و في حالتنا النص عبارة عن مصفوفة من نوع char فإن. حجم العنصر هو حجم من نوع بيانات char أي 1 بايت و هو الحالة في اغلب حالات كتابة النصوص في الملفات,. و لكن في هذا توجد مشكلة بسيطة و هي أن أحجام أنواع البيانات بشكل عام قد تختلف من معمارية جهاز إلى آخر. و لهذا كتبنا الدالة sizeof من باب الإحتياط و هي تماماً كأننا كتبنا 1 في هذا الجهاز. ثم كتبنا char ليتم إعطائنا حجم البيانات عندما نقوم بكتابته في هذا الملف.
و المعامل الثالث في دالة fwrite هو رقم يُمثِّل عدد العناصر التي سيتم كتابتها في الملف, و بما أن كل عنصر هو عبارة عن char إذاً عدد العناصر هو فعلياً عدد أحرف النص. يمكننا إما عد عدد الأحرف و كتابة الناتج أو اللجوء إلى دالة strlen الموجودة في مكتبة string.h وهذا هو الحل الأمثل بدلاً من عَد. حرف حرف ثم قُمنا بتمرير النص و هو s ثم زودنا الدالة بمؤشر الملف وهو f. ثم بعد ذلك قُمنا بإغلاق الملف لأننا إنتهينا منه عبر إستخدام دالة fclose و هي تأخذ معاملاً واحداً و هو مؤشر الملف الذي قمنا بفتحه وهو f.
عند تشغيل هذا البرنامج سوف يتم إنشاء الملف بشكل تلقائي و الكتابة بداخله أو في حال كان الملف موجود سوف يتم الكتابة عليه. سنقوم الآن بتشغيل البرنامج لنرى الناتج أنه أنشأ لنا ملف على سطح المكتب كما يلي.
كما نرى أن الملف تم إنشاءهُ في منتصف الشاشة, و عندما نقوم بفتح هذا الملف سوف نجد به ما تمت كتابته أثناء كتابة الكود
القراءة من ملف في لغة السي C
سوف نعتمد على المثال السابق و نقوم بالقراءة منه, نقوم في البداية بحذف. جميع الأكواد التي كتبناها في المحرر مسبقاً و نبدأ بكتابة كود القراءة من ملف نصي في لغة السي C.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *f = fopen("/home/data/Desktop/DBCS.txt", "r");
fseek(f, 0, SEEK_END);
unsigned int sz = ftell(f);
fseek(f, 0, SEEK_SET);
char *data = (char *)malloc(sz);
fread(data, sizeof(char), sz, f);
printf("File has been printed \n%s", data);
free(data);
fclose(f);
return 0;
}
لقد قمنا بإنشاء مؤشر pointer من نوع FILE و نتأكد هنا أننا كتبناها باﻷحرف الكبيرة Capital و المؤشر هو f و كتبنا الدالة fopen من أجل فتح الملف الموجود في جهاز الكمبيوتر. وحددنا مساره في سطح المكتب و إسمه DB ثم حددنا نوع العملية و هي القراءة عن طريق كتابة "r". يُمكننا إستعمال دالة fread للقراءة لكن في حالة القراءة تنقُصنا عملية إضافية و هي مَعرِفة حجم الملف الذي نود القراءة منه. و ذلك لأننا سنفوم بحجز ذاكرة تتسع للمحتوى الذي نود قراءته,. لذلك يجب علينا معرفة سعة الذاكرة المحجوزة لهذا الملف.
و لمعرفة حجم الملف سنقوم بالإستعانة بالدالتين fseek و ftell حيث سنقوم اولاً بالإنتقال عبر دالة fseek للإنتقال إلى نهاية الملف. و الدالة ftell ستٌخبرنا بمكانه الفعلي في الملف أي عدد البايت Bytes من بداية الملف و بالتالي نكون قد عرفنا حجم البايتس في الملف. تأخذ دالة fseek ثلاثة معاملات, في البداية نقوم بتمرير لها الملف الذي نريد القراءة منه وهو f و المعامل الثاني. هو قيمة عددية لتحديد موقعنا في الملف و كتبناه 0 لسبب سنعرفه في المعامل الثالث.
المعامل الثالث يقبل واحد من ثلاث حالات و هي :
الحالة الأولى : SEEK_SET و هذه العبارة تعني أننا نُريد الإنتقال تماماً إلى الموقع المُشار في المعامل الثاني وهو 0. فلو أبقينا المعامل الثاني 0 سوف ينقُلنا لبداية الملف و لو جعلناه 1 سينقلنا للحرف الأول في الملف أو البايت الأول. لأننا نستخدم نظام آسكي ASCII و هذه الحالة لا تخدمنا في المثال و نحن نُريد الإنتقال لآخر الملف لا بدايته.
الحالة الثانية : SEEK_CUR هي تقوم بالإنتقال من مكاننا الحالي إلى الأمام بمقدار العدد المُحدد في المعامل الثاني. فلو أبقينا المعامل الثاني 0 فلن يكون لجملة fseek أي تأثير أما إذا كان 1 سيتم الإنتقال خطوة للأمام و هذه الحالة ايضاً لا تخدمنا هنا.
الحالة الثالثة : SEEK_END و تقوم بالإنتقال إلى آخر الملف و هي التي نحتاجها حالياً في البرنامج. وبما أننا أصبحنا في آخر الملف سوف نستخدم ftell لتُخبرنا بموقعنا في الملف و بما أننا في آخر الملف فموقعنا. يُساوي حجم الملف يعني البايتس الأخير و تأخذ مُعامل واحد فقط و هو مؤشر الملف f مسار الملف.
و تقوم الدالة ftell بإرجاع قيمة و هذه القيمة تكون نوع unsigned int و سنقوم بتخزين. هذه القيمة في مُتغير بإسم sz مثلاً, الآن اصبح حجم الملف مخزناً في متغير sz. و إستخدمنا الدالة fseek مُجدداً للرجوع لبداية الملف عبر SEEK_SET أي إنتقلنا لبداية الملف للموقع 0. ثم إستخدمنا دالة القراءة fread للقراءة و المعامل الأول هنا سيكون مؤشر للمكان في الذاكرة الذي ننوي بتخزين البيانات المقروءة فيه.
سنقوم بتعريف متغير data من نوع pointer و يساوي malloc و بها قُمنا بحجز السعة اللازمة لقراءة جميع البيانات الموجودة في الملف و تخزينها في المُتغير data. و السعة هي sz تأخذ حجم الملف الذي قُمنا بفتحه و الدالة malloc تقوم بإرجاع pointer إلى void و نقوم بتحويل هذا المؤشر لنوع البيانات char* الذي نقوم بتخزينه. ثم بدأنا بقراءة الملف عن طريق الدالة fread عن طريق إستعمال المؤشر data و المُعامل الثاني يُمثل حجم العنصر الواحد للمحتوى المقروء. و سيكون حجم نوع البيانات char أي بايت واحد و المعامل الثالث يمثّل عدد العناصر المقروءة و هو sz و المعامل الرابع الأخير هو الملف الذي قٌمنا بفتحه وهو f.
أخيراً نقوم بطباعة الملف المقروء الذي تم تخزينه في المتغير data الذي خزناه سابقاً عن طريق كتابة نص نريده File has been printed و %s ثم النص الذي تمت قراءته,. ثم اغلقنا الملف و كتبنا free من أجل إلغاء الحجز للمتغير data الذي قُمنا بحجز الذاكرة له عن طريق الدالة malloc.