二進制文件
二進制文件非常類似于結(jié)構(gòu)體數(shù)組,只不過這些結(jié)構(gòu)體被保存在一個磁盤文件而非內(nèi)存數(shù)組中。因為是使用磁盤保存二進制文件中的結(jié)構(gòu)體,所以您可以創(chuàng)建非常龐大數(shù)目的結(jié)構(gòu)體(只受可用磁盤空間的限制)。它們還是永久性的,并且可以隨時使用。惟一的缺點是磁盤存取會造成延遲。
二進制文件與文本文件有兩個不同的特點:
- 您可以立即跳至文件中的任一結(jié)構(gòu)體,類似于數(shù)組的隨機存取。
- 您可以隨時改變文件中任一處結(jié)構(gòu)體的內(nèi)容。
二進制文件通常還具有比文本文件更短的存取時間,因為文件記錄的二進制映像是直接從內(nèi)存?zhèn)魉椭链疟P的(或相反的方向)。對于文本文件,所有數(shù)據(jù)都要反復轉(zhuǎn)換成文本,而這需要花費時間。
C所支持的“結(jié)構(gòu)體文件”概念十分簡潔。某文件被打開后,您可以讀取一個結(jié)構(gòu)體,寫入一個結(jié)構(gòu)體,或移動至文件中的任一結(jié)構(gòu)體。這種文件模型要求有一個文件指針的概念。打開文件時,指針指向0號記錄(文件的第一個記錄)。任何讀操作都讀取當前被指向的結(jié)構(gòu)體,并將指針指向下一個結(jié)構(gòu)體。任何寫操作都向當前被指向的結(jié)構(gòu)體寫入數(shù)據(jù),并將指針指向下一個結(jié)構(gòu)體。移動操作將文件指針移至指定的記錄。
請記住C總是將文件內(nèi)容視為從磁盤讀入內(nèi)存或從內(nèi)存寫入磁盤的字節(jié)塊。C使用文件指針,但指針可以指向文件中的任一字節(jié)。因此您需要自己管理好指針的位置。
下面的程序可以說明以上概念:
?
#include\ /* 任取一種文件記錄結(jié)構(gòu),也可以是其他形式 */ struct rec { int x,y,z; }; /* 向文件“junk”先寫入 再讀取10條隨意的記錄。*/ int main() { int i,j; FILE *f; struct rec r; /* 創(chuàng)建一個包含10條記錄的文件 */ f=fopen("junk","w"); if (!f) return 1; for (i=1;i<=10; i++) { r.x=i; fwrite(&r,sizeof(struct rec),1,f); } fclose(f); /* 讀取這10條記錄 */ f=fopen("junk","r"); if (!f) return 1; for (i=1;i<=10; i++) { fread(&r,sizeof(struct rec),1,f); printf("%d \ n ",r.x); } fclose(f); printf(" \ n "); /* 使用fseek逆序讀取10條記錄 */ f=fopen("junk","r"); if (!f) return 1; 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 "); /* 使用fseek隔條讀取記錄 */ f=fopen("junk","r"); if (!f) return 1; 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 "); /* 使用fseek讀取第4條記錄, 修改記錄內(nèi)容并寫回 */ f=fopen("junk","r+"); if (!f) return 1; 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 "); /* 讀取10條記錄 檢查第4條記錄是否已被修改 */ f=fopen("junk","r"); if (!f) return 1; for (i=1;i<=10; i++) { fread(&r,sizeof(struct rec),1,f); printf("%d n ",r.x); } fclose(f); return 0; }
此程序使用了一個名為rec的結(jié)構(gòu)體類型,但您也可以使用任一種結(jié)構(gòu)體類型。您可以看到fopen和fclose的使用和在文本文件中是一樣的。
新引入的函數(shù)是fread、fwrite和fseek。fread函數(shù)接受四個參數(shù):
- 一個內(nèi)存地址
- 讀入的內(nèi)存塊包含的字節(jié)數(shù)
- 讀入的內(nèi)存塊個數(shù)
- 文件變量
因此,fread(&r,sizeof(struct rec),1,f);表示:把12個字節(jié)(rec類型的大?。┑膬?nèi)容從文件f(文件指針指向的當前位置)讀入內(nèi)存地址&r,共要求讀入一個12字節(jié)大小的塊。只要把1改成100,就可以很容易地使這條語句變?yōu)椋簩?00個塊從磁盤讀入一個內(nèi)存數(shù)組中。
fwrite和fread類似,只不過它是將字節(jié)塊從內(nèi)存寫入文件中。fseek函數(shù)負責把文件指針移至文件中的某個字節(jié)。指針每次移動的距離一般都是sizeof(struct rec)的整數(shù)倍,這樣指針就可以保持總是指向記錄的開始處。移動指針有三種方式:
- SEEK_SET
- SEEK_CUR
- SEEK_END
SEEK_SET表示指針從文件開始處(0字節(jié)處)向后移動x個字節(jié)。SEEK_CUR表示指針從當前位置向后移動x個字節(jié)。SEEK_END表示指針從文件末尾向前移動(所以偏移量應為負數(shù))。
上面代碼中使用了多個函數(shù)選項。其中請?zhí)貏e注意一下用r+模式打開文件的段落。這種模式支持文件的讀取和寫入,即可以修改文件中的記錄。程序首先把文件指針移至某個記錄,然后讀取這條記錄內(nèi)容并修改了一個成員。之后重新把指針移動指向此記錄,因為剛才的讀取已經(jīng)更新了指針。最后把修改過的記錄寫回。
評論