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