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