C/C++ מדריכי
C הקדמה לתכנות ב
C++ הקדמה להיררכית מחלקות ב הצגה מזורזת :C++ הבנת דף הבית

שיעור 16: קבצים בינאריים ב C

קבצים בינאריים דומים מאוד למערכים של רשומות, אלא שהרשומות נמצאות בקובץ דיסק במקום במערך בזיכרון. כיוון שהרשומות בקובץ בינארי הן בדיסק, ניתן ליצור אוסף גדול שלהן (מוגבל רק על ידי גודל הזיכרון הפנוי בדיסק שלך). הן גם קבועות וזמינות תמיד. החיסרון היחיד היא האיטיות שנובעת מזמן גישה לדיסק.

לקבצים בינאריים יש שתי תכונות שמבדילות אותם מקבצי טקסט: ניתן לקפוץ מיד לכל רשומה בקובץ - דבר שמספק גישה אקראית כמו במערך, וניתן לשנות את התוכן של רשומה בכל מקום בקובץ בכל זמן. לקבצים בינאריים גם יש בדרך כלל זמני קריאה וכתיבה מהירים יותר מקבצי טקסט, כיוון שהצורה הבינארית של רשומה מאוחסנת ישירות מזיכרון לדיסק (או להפך). בקובץ טקסט, ניתן להמיר הכל הלוך ושוב, וזה לוקח זמן.

פסקל תומכת ברעיון של קובץ רשומות בצורה נקייה מאוד. אתה מצהיר על משתנה כגון
var f:file of rec; ואז פותח את הקובץ. בנקודה זו, אתה יכול לקרוא רשומה, לכתוב רשומה או לחפש רשומה כלשהי בקובץ. מבנה קובץ זה תומך ברעיון של מצביע קובץ. כאשר קובץ נפתח, המצביע מצביע לרשומה 0 (הרשומה הראשונה בקובץ). כל פעולת קריאה קוראת את הרשומה שאליה המצביע מצביע באותו זמן ומקדמת את המצביע ברשומה אחת. חיפוש מזיז את המצביע לרשומה המבוקשת. ב C, הרעיונות הם בדיוק אותו הדבר אבל פחות תמציתיים. זכור ש C מסתכלת על כל דבר בקובץ הדיסק כגושים של בתים שנקראו מהדיסק לזיכרון או מהזיכרון לדיסק. C משתמשת במצביע קובץ, אבל הוא יכול להצביע לכל בית בקובץ. התוכנית הבאה מדגימה את הרעיונות האלו:

#include <stdio.h> 
 
/* random record description - could be anything */  
struct rec  
{        
    int x,y,z;  
}; 
 
/* writes and then reads 10 arbitrary records from the file "junk". */ 
void main()  
{    
    int i,j;    
    FILE *f;    
    struct rec r; 
     
    /* create the file of 10 records */    
    f=fopen("junk","w");    
    for (i=1;i<=10; i++)    
    {      
        r.x=i;      
        fwrite(&r,sizeof(struct rec),1,f);    
    }    
    fclose(f);      
     
    /* read the 10 records */     
    f=fopen("junk","r");    
    for (i=1;i<=10; i++)    
    {      
        fread(&r,sizeof(struct rec),1,f);      
        printf("%d\n",r.x);    
    }    
    fclose(f);    
    printf("\n");       
    
    /* use fseek to read the 10 records in reverse order */ 
    f=fopen("junk","r");    
    for (i=9; i>=0; i--)    
    {      
        fseek(f,sizeof(struct rec)*i,SEEK_SET);      
        fread(&r,sizeof(struct rec),1,f);      
        printf("%d\n",r.x);    
    }    
    fclose(f);    
    printf("\n"); 
    
    /* use fseek to read every other record */  
    f=fopen("junk","r");    
    fseek(f,0,SEEK_SET);    
    for (i=0;i<5; i++)    
    {      
        fread(&r,sizeof(struct rec),1,f);      
        printf("%d\n",r.x);      
        fseek(f,sizeof(struct rec),SEEK_CUR);    
    }    
    fclose(f);    
    printf("\n"); 
    
    /* use fseek to read 4th record, change it, and write it back */    
    f=fopen("junk","r+");    
    fseek(f,sizeof(struct rec)*3,SEEK_SET);    
    fread(&r,sizeof(struct rec),1,f);    
    r.x=100;    
    fseek(f,sizeof(struct rec)*3,SEEK_SET);  
    fwrite(&r,sizeof(struct rec),1,f);     
    fclose(f);    
    printf("\n"); 
   
    /* read the 10 records to insure 4th record was changed */     
    f=fopen("junk","r");    
    for (i=1;i<=10; i++)    
    {     
        fread(&r,sizeof(struct rec),1,f);      
        printf("%d\n",r.x);    
    }    
    fclose(f);     
} 

בתוכנית זו, נעשה שימוש בתיאור רשומה חסרת משמעות rec, אבל ניתן להשתמש בכל תיאור רשומה שרוצים. ניתן לראות ש fopen ו fclose פועלות בדיוק כפי שפעלו בקבצי טקסט.

הפונקציות החדשות כאן הן fwrite ,fread, ו fseek. הפונקציה fread מקבלת ארבעה פרמטרים: כתובת זיכרון, מספר בתים לקרוא בבלוק, מספר הבלוקים לקרוא, ומשתנה קובץ. כך השורה
fread(&r, sizeof(struct rec), 1, f); אומרת לקרוא 12 בתים (הגודל של rec) מהקובץ f (החל מהמיקום הנוכחי של מצביע הקובץ) לתוך כתובת הזיכרון r&. גוש אחד של 12 בתים נדרש. יהיה קל באותה מידה לקרוא 100 גושים מדיסק למערך בזיכרון באמצעות שינוי ה 1 ל 100.

הפונקציה fwrite פועלת באותה צורה, אבל מזיזה את גוש הבתים מזיכרון לקובץ. הפונקציה fseek מזיזה את מצביע הקובץ לבית בקובץ. באופן כללי, מזיזים את המצביע ברווחים של sizeof(struct rec) כדי להשאיר את המצביע בגבולות רשומה. ניתן להשתמש בשלוש אפשרויות בחיפוש: ,SEEK_CUR ,SEEK_SET ו SEEK_SET. SEEK_END מזיזה את המצביע קדימה x בתים מההתחלה של הקובץ (מבית 0 בקובץ). SEEK_CUR מזיזה את המצביע קדימה x בתים מהמיקום הנוכחי של המצביע. SEEK_END מזיזה את הקובץ מסוף הקובץ (כך שיש להשתמש בהיסט שלילי עם אפשרות זו).

מספר אפשרויות שונות מופיעות בקוד שלעיל. בפרט, שים לב לקטע שבו הקובץ נפתח עם מצב +r. זה פותח את הקובץ לקריאה וכתיבה, דבר שמאפשר לשנות את הרשומות. הקוד מחפש רשומה, קורא אותה, ומשנה שדה, לאחר מכן הוא מחפש חזרה כיוון שהקריאה הזיזה את המצביע, וכותב את השינוי חזרה.




לדף הראשון

<< לדף הקודם