الملفات مع السي

 السلام عليكم و رحمة الله و بركاته

الملفات مع السي
 

هل فكرت يوماً بعمل برنامج ولنقل مثلاً لمكتب عقاري ؟
هل فكرت يوماً بعمل برنامج يقوم بعمل معين ولكنك تحتاج إلى البيانات الموجوده فيه مسبقاً و لا تريدها ان تضيع بمجرد إقفال البرنامج ؟

الحل سيكون إستخدام الملفات بالتأكيد ...فهي المعنى الحقيقي للبرمجة و هي التي تقوم بجعل البرامج العديمة الفائدة مفيدة جداً , فتخيل برنامج Word مثلاً بدون الملفات ؟!!! لن يكون له اي فائدة عدى القليل جداً .

طبعاً اقصد بالملفات هي الحفظ و الفتح التي تقوم بها يوميا تقريباً في برامجك .

عموماً لن نطيل في هذه المقدمات كثيراً و لنبداء البرمجة بإستخدام الملفات :



الملفات في أي لغة لن تتعامل معها مباشرة ( أي بإسم الملف ) و لكنك ستتعامل مع مؤشر إلى الملف و لنبدأ:

 أولاً كيف يمكننا أن نعرف مؤشر إلى ملف ؟

________________________________________________________________________________


1. التعريف :
 

FILE *file_pointer;

 مع العلم ان كلمة FILE لابد ان تكون بالحروف الكبيره و file_pointer هي كلمة إختيارية تختارها أنت و لتكن مثلاً fp لسهولة الإستعمال و لكن لابد أن تسبق بالعلامة *
فقط في التعريف ( كما هو موضح في الأعلى ) أي أن المتغير سيكون مؤشر للملف.
 

__________________________________________________________________________________


2. فتح الملف :
لابد من فتح الملف قبل إستخدامة مثلة مثل فتح ( تشغيل ) الكمبيوتر قبل إستخدامة
ولفتح الملف سنستعمل الدالة التالية :


FILE *fopen ( const char *filename, const char *mode ) ;


الـ filename هو إسم الملف الذي يكون ظاهر للمستخدم مثلاً talal.txt
و الـ mode هو طبيعة فتح الملف فهناك اكثر من صفة لفتح الملف سأذكرها بعد قليل
فلو كان لدينا هذا التعريف :
 

FILE *fp;
 

و نريد ان نفتح ملف إسمة talal.txt فسنفتحه بالطريقة التالية :
 

fp = fopen( "talal.txt", "w" ) ;
 

إذا لم يتمكن النظام من فتح الملف سنعيد في الـ fp قيمة NULL .
و لنرى الصفات التي تفتح عليها الملفات :

 

 

الطريقة الوصف
r open a text file for reading   أي فتح ملف للقراءة
w open a text file for writing   أي فتح ملف للكتابة و إذا كان الملف موجود يقوم بمسح محتوياته
a (open a text file for appending (adding info to the END of file    فتح للإضافة إلى النهاية
+r ( opening a text file for update ( reading and writing  فتح ملف للتحديث ( قرائة و كتابة )
+w open a text file for update. If the file exists it is truncated to zero length. If it does not exists it is created

 يفتح ملف للتعديل ( للقرائة و الكتابة ) إذا كان الملف موجود فإنة تلغى محتوياته
أما إن لم يكن موجود فإن النظام ينشأة

+a open a text file for update ( reading and writing ). Writing is only allowed at the end of file. But any part of the file can be readed. If the file does not exists the system create it

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

Adding a 'b' changes the file type from text to binary. Apart from that the string will have the same effect.

و عند إضافة الحرف 'b' إلى نوع الفتح سيتغير من فتح ملف من نوع text إلى فتح من نوع binary. و سيكون لملف الـ Binary نفس خصائص الحروف بالـ text .

 






إذاً إذا أردنا أن نفتح ملف للقرائة منه ماذا نفعل ؟
الجواب هو كالتالي :


fp = fopen("talal.txt", "r");


و إذا أردنا فتح الملف للقراءة و الكتابة معاً يمكننا ذلك بطرق أحدها:

 

fp = fopen("talal.txt", "r+");   // OR by using w+ OR using a+

 
________________________________________________________________________________


3. إغلاق الملف :


كما فتحنا الملف في البداية لابد من اإغلاقة عند الإنتهاء منه أليس كذلك ؟!!
بالتأكيد سنغلقة في حال إنتهينا من إستعمال الملف و الدالة المسؤلة عن إغلاق الملف هي:
 

int fclose(FILE *file_pointer );
 

هكذا فقط :) .
فلو كان عندنا التعريف التالي :
 

FILE *fp ;
fp = fopen("talal.txt", "w") ;
 

سنغلق هذا الملف هكذا :
 

fclose ( fp ) ;
 

فقط, هذا كل ما في الأمر. أليس هذا سهلاً ؟!!


وإذا لم ينفتح الملف لأي سبب من الأسباب كأمتلاء الذاكرة, ستعيد الدالة القيمة NULL.

_________________________________________________________________________________

4. الإدخال و الإخراج من الملفات ( I/O with files ) :

الآن قد انتهينا من  كيفية فتح و إغلاق الملفات إلى هذه النقطة لم نتعلم كيف نحفظ ما قمنا بعمله, للقيام بذلك هناك عدة دوال مسؤالة عن هذا الشيء منها:

الدالة الأولى هي :
 

int putc( char ch, FILE *file_pointer );
 

و هي موجوده ضمن ملف stdio.h .
و لنأخذ مثال على هذه الدالة :
 

#include "stdio.h"
main()
{
    FILE *fp ;
    char ch ;
        fp = fopen("talal.txt", "w");
        while( ( ch = getche() ) != 'r' )
            putc(ch, fp);

        fclose(fp) ;
    return 0 ;
}
 

لقد قمت بتنفيذ هذا البرنامج فوجدت ملف إسمة talal.txt في نفس المجلد الذي يوجد به الكود الذي نفذتة فوجدت بداخلة الجملة التي كتبتها و هي :
 

Hi this is Talal


بعد تنفيذ البرنامج الأول لنقم بكتابة هذا الكود بعده :

 

#include "conio.h"
#include "stdio.h"
main()
{
    FILE *fp ;
    char ch ;
        fp = fopen("talal.txt", "r");
        while( (ch = getc(fp) ) != EOF )
            printf("%c",ch);

        fclose(fp) ;
    return 0 ;
}
 

هذا الكود يقوم بقرائة الملف و طباعة محتواه على الشاشة , و لكن ما هي و ظيفة الدالة getc ؟ هذه دالة للقرائة من ملف و هي على الصيغة التالية :
 

char getc( FILE *file_pointer ) ;
 

و هي توجد في الملف stdio.h , ولشرح هذا البرنامج بالتفصيل :
عرفنا مؤشر إلى ملف بإسم fp , ثم قمنا بفتح الملف بإستخدام الدالة fopen و فتحناه على صيغة قرائة من ملف 'w' , الان مؤشر الملف على اول حرف في الملف , ثم دخلنا الدوارة while فنقراء أول حرف و نضع قيمتة في الـ ch و نقارنة بالقيمة EOF و هي تعني
End Of File ( نهاية الملف ) فنستمر في القرائة من الملف و نطبع على الشاشة قيمة الـ ch إذا لم تكن EOF .

________________________________________________________________________________

الدالة :
 

fprintf (file_pointer, " ",,,) ;
 

وهي مثل الدالة printf تماماً ولكنها تختلف في أن البراميتر الأول هو مؤشر الملف و مثلها
أيضاً الدالة :
 

fscanf (file_pointer, " " ,,,) ;
 

وهي مثل الدالة scanf تماماً و لكن البراميتر الأول مؤشر للملف الذي نريد أن نقرأ منه .

لنأخذ هذا البرنامج على الدالتين السابقتين :
 

#include "stdio.h"
main()
{
    FILE *fp;
    char name[40];
    int id;
    float gpa;

    fp = fopen("std.txt","w");

    if(fp == NULL)
        printf("nCan't Open File...");
    else
    {
        do
        {
            printf("nEnter Name, id, gpa: ");
            scanf("%s %d %f",name, &id, &gpa);
            fprintf(fp,"%s %d %.2fn",name, id, gpa);
        }while( id != 0 );
    }
    fclose(fp);
return 0;
}
 

في هذا المثال فتحنا الملف STD.txt في الأول, و تأكدنا أن الملف لم يرجع NULL  (أي أن الملف قد فتح)
بالشرط أعلاه :
 

if(fp == NULL)
printf("nCan't Open File...");
 

و هذا الشرط مهم جداً لعمل برنامج كامل يعمل في جميع الأحوال.
بعد ذلك عملنا دواره do…..While حتى يدخل المستخدم رقم صفر لأي طالب.
بعد ذلك قمنا بالطلب من المستخدم أن يدخل الإسم و الرقم و المعدل GPA, و إستقبلنا المدخلات بالأمر scanf, و حتى هنا لا يوجد شئ جديد, بعدها قمنا بالطباعة في الملف بالأمر fprintf
و بعدها قمنا بإغلاق الملف.

ثم بعد ذلك سنكتب البرنامج التالي:
 

#include "stdio.h"
main()
{
        FILE *fp;
        char name[40], ch;
        int id;
        float gpa;
        fp = fopen("std.txt","r");

        if(fp == NULL)
            printf("nCan't Open File...");
        else
        {
            fscanf(fp, "%s %d %f",name, &id, &gpa);
            fscanf(fp, "%c", &ch);

            while( id != 0 )
            {
                printf("Name:%s, ID:%d, GPA:%.2fn",name, id, gpa);
                fscanf(fp,"%s %d %f",name, &id, &gpa);
                fscanf(fp, "%c", &ch);
            }
        }
        fclose(fp);
    return 0;
}
 

ماذا سنجد ؟!؟
سنجد المعلومات التي كتبناها في البرنامج الذي سبق هذا :) .
و لكن قد يسئل البعض لماذا وضعنا هذه الجملة :
 

fscanf(fp, "%c", &ch);
 

اقول أولاً لنرجع للبرنامج الذي سبق هذا و في الجملة التالية بالذات :
 

fprintf(fp,"%s %d %.2fn",name, id, gpa);



أقول اننا طبعا سطر جديد في الملف بالأمر n و السطر الجديد هو عبارة عن char
فكما طبعنا في الملف سطر جديد لا بد أن نقرأ هذا الحرف بالأمر:
 

fscanf(fp, "%c", &ch);
 

فجرب ان تزيل هذه الجملة و سترى النتيجة :) .

و هناك ايضاً بعض الدوال التي سنكتفي بذكر تعريفها فقط :
 

char *fgets ( char *s, int n, FILE *File_pointer) ;
 

وهذه الدالة تقراء في المتغير s حتى الحرف n-1 أو حتى نهاية السطر .
 


int fputs ( const char *s, FILE *FILE_pointer ) ;



وهاتان الدالتان مثل الدوال gets و puts تماماً غير انها للملفات .

والدوال السبقة هي دوال للملفات المنتظمة أي تقراء و تطبع سطر بسطر بالترتيب .

________________________________________________________________________________



أما الآن فسندخل في الملفات العشوائية Random Files :
و لملفات العشوائية يوجد عدد من الدوال لذلك وهي غالباً للكاتبة و القرائة بالسجلات Structures
و من هذه الدوال :
 

size_t fread ( void *ptr, size_t size, size_t n, FILE *file_pointer ) ;
 

فالباراميتر الأول يأخذ السجل المراد القرائة فيه و المتغير الثاني يأخذ حجم السجل و المتغير
الثالث يأخذ غالباً الرقم ( واحد ) أي عدد السجلات في كل مرة و المتغير الرابع يأخذ مؤشر
الملف , و سنأخذ مثال على هذه الدالة و الدوال الباقية مجتمعه في مثال واحد .

الدالة الثانية :
 

size_t fwrite(const void *ptr, size_t size, size_t n, FILE *file_pointer ) ;
 

وهي نفس الدالة السابقة تماماً ولكن هذه الدالة للكتابة فقط .

الدالة الثالثة :
 

int fseek (FILE *File_pointer, long offset, int whence );
 

وهذه الدالة تستخدم في التنقل عبر الملف, فالمتغير الأول عبارة عن مؤشر للملف المراد التنقل فيه , و المتغير الثاني عبارة عن عدد الحروف المراد قفزها, و المتغير الثالث عبارة عن مكان القفز و هو أما ان يكون من بداية الملف SEEK_SET أو من المكان الحالي SEEK_CUR
او من النهاية SEEK_END .

فلو كان عندنا السجل التالي :
 

typedef struct
{
     datatype data;
}RECORD;
 

,ومن ثم عرنا متغير R من نوع RECORD كالتالي :
 

RECORD R ;
 

و كتبنا في الملف و لنقل 100 سجل ...
و اردنا أن نذهب للسجل الأخيرفي الملف فكيف يكون ذلك ؟!
الحل سيكون بالأمر fseek ولكن هل سيكون كالتالي : ؟!
 

fseek ( fp, 0, SEEK_END ) ;
 

و لنفرض جدلاً ان هذا الكلام صحيح !! لنترجم هذا الامر لنعرف صحتة من خطأه ...
الامر يقول اقفز من نهاية الملف الذي يؤشر عليه fp بمقدار صفر بايت .
إذاً سيكون المؤشر عندها على نهاية الملف و ليس على آخر سجل :)
و لنجعله يقف على آخر سجل سنغير الامر كالتالي :
 

fseek ( fp, - sizeof(RECORD) , SEEK_END ) ;
 

يعني علمنا الآن ان امقدار السالب يعمل على الإرجاع للخلف :) .

أما الان سنأخذ الدالة :
 

long ftell ( FILE *File_pointer ) ;
 

و هي تعطينا عدد البايتات من بداية الملف حتى موضع مؤشر الملف, ومن اهم إستخداماتها هي
معرفة عدد السجلات في الملف و لنأخذ هذه الدالة التي أنشأناها كمثال لهذه العملية :
 

int size ( FILE *fp)
{
        int c;

        fseek( fp, 0, SEEK_END) ;
        c = ftell(fp) ;
        c = c / sizeof(RECORD) ;
    return c ;
}
 

وهذه الدالة تعيد لنا عدد السجلات في الملف و اعتقد انها قد اوضحت جيداً كيفية إستعمال الدالة ftell .

والان لنأخذ مثال عام على الملفات العشوائية و تبين كيفية القرائة و الكتابة من الملف
و تبين كيفية إستخدام الدالة size التي كتبناها أعلاه و أليكم هذا المثال:
 

#include "stdio.h"
#include "stdlib.h"
//-------------------------------------
struct data
{
    int id;
    float gpa;
};
//-------------------------------------
int size(FILE *fp)
{
        int c;

        fseek(fp, 0, SEEK_END);
        c = ftell(fp);
        c = c / sizeof(struct data);
    return c ;
}
//-------------------------------------
void readrec(struct data *r)
{
    printf("n Enter the ID No: ");
    scanf("%d",&r->id);
    printf("n Enter the GPA: ");
    scanf("%f",&r->gpa);
}
//-------------------------------------
main()
{
        FILE *fp;
        struct data r;
        int ch=0 ;

        fp=fopen("struct.txt","w");

        printf("n Enter (1)to write. (0)to EXIT.");
        scanf("%d",&ch);

        while(ch!=0)
        {
            readrec(&r);

            fwrite(&r,sizeof(struct data),1,fp);
            system("cls");
            printf("n Enter (1)to write. (0)to EXIT.");
            scanf("%d",&ch);
        }


        fclose(fp);

        fp=fopen("struct.txt","r");

        while((fread(&r,sizeof(struct data),1,fp)) != 0)
        {
            printf("nID:%dttGPA:%.2f",r.id,r.gpa);
        }

        printf("nnnThe File Contane: %d Record..",size(fp));

        fclose(fp);
        printf("nn");

}

 


و للزيادة أيضاً هناك الدالة :
 

remove(char *file_name) ;
 


و تعطى هذه الدالة String الذي هو إسم الملف أو مكانه و تقوم هذه الدالة
بحذف الملف من الجهاز .

 

ملاحظه:

أي دالة يكون أحد البراميتر التي ترسل لها هو إسم ملف مثل fopen أو remove نستطيع إرسال اسم الملف كــ Path أي  هكذا:

remove("C:\talal\talal.txt");

بشطر أن نكتب بدل العلامة '' ---->  '\' كما هو موضح في الأعلى.
 

 



آمل أني قد وفقت في الشرح و لا تنسونها من دعائكم ,,,

مع تحياتي أخوكم / طلال .
 

 

 

 


Copyright © www.kettaneh.net