計算機文件是以計算機硬盤為載體存儲在計算機上的信息集合,是存儲在某種長期儲存設備上的一段數據流。在C語言中用一個指針變量指向一個文件,這個指針稱為文件指針。通過文件指針就可對它所指的文件進行各種操作,C語言規定該文件指針類型為FILE型。文件的信息有這個FILE型的結構體來進行定義。
FILE的結構體的定義如下
typedef struct { short level; // 緩沖區“滿”或“空”的程度 unsigned flags; // 文件狀態標志 char fd; // 文件描述符 unsigned char hold; // 如緩沖區無內容不讀取字符 short bsize; // 緩沖區的大小 unsigned char *buffer; // 數據緩沖區的位置 unsigned ar *curp; // 指針當前的指向 unsigned istemp; // 臨時文件指示器 short token; // 用於有效性檢查 }FILE;
例如我們定義一個文件指針:FILE *fp; fp指向某一個文件的文件信息區(是一個結構體變量),通過該文件信息區就能夠訪問該文件。如下圖所示:
文件緩沖區:緩沖文件系統是指系統自動地在內存區為程序中每一個正在使用的文件開辟一個文件緩沖區。從內存向磁盤輸出數據必須先送到內存中的緩沖區,裝滿緩沖區后才一起送到磁盤中去。
文件的打開(fopen函數)
函數調用:
FILE *fp;
fp = fopen(文件名,使用文件方式);
注意:
需要打開的文件名,也就是准備訪問的文件的名字
使用文件的方式(“讀”還是“寫”等);
讓哪一個指針變量指向被打開的文件。
文件使用方式
含 義
-------------------------------------------------------------------------------------
“r” (只讀)為輸入打開一個文本文件
“w” (只寫)為輸出打開一個文本文件
“a” (追加)向文本文件尾增加數據(以ascll碼的形式追加)
“rb” (只讀)為輸入打開一個二進制文件
“wb” (只寫)為輸出打開一個二進制文件
"ab“ (追加)向二進制文件尾增加數據(以二進制的形式追加)
"r+“ (讀寫)為讀/寫打開一個文本文件
"w+” (讀寫)為讀/寫建立一個新的文本文件
"a+” (讀寫)為讀/寫打開一個文本文件
"rb+“ (讀寫)為讀/寫打開一個二進制文件
“wb+“ (讀寫)為讀/寫建立一個新的二進制文件
“ab+” (讀寫)為讀/寫打開一個二進制文件
-------------------------------------------------------------------------------------
幾點注意:
凡用“r”打開一個文件時,該文件必須已經存在,且只能從該文件讀出。
文件若不存在發生錯誤。
用“w”打開的文件只能向該文件寫入。若打開的文件不存在,則以指定的文件名建立該文件,若打開的文件已經存在,則將該文件刪去,重建一個新文件。
若要向一個已存在的文件追加新的信息,只能用“a”方式打開文件。但此時該文件必須是存在的,否則將會出錯。
在打開一個文件時,如果出錯,fopen將返回一個空指針值NULL。
在程序中可以用這一信息來判別是否完成打開文件的工作,並作相應的處理。
把一個文本文件讀入內存時,要將ASCII碼轉換成二進制碼,而把文件以文本方式寫入磁盤時,也要把二進制碼轉換成ASCII碼,因此文本文件的讀寫要花費較多的轉換時間。對二進制文件的讀寫不存在這種轉換。
fclose 函數說明:
作用:關閉一個文件流,釋放文件指針
格式:int fclose( FILE *fp );
返回值:如果流成功關閉,fclose 返回 0,否則返回EOF
參數說明:
*fp:需要關閉的文件指針
注:在文件操作完成后我們應該調用該函數來關閉文件,如果不關閉文件將可能會丟失數據。因為在向文件寫入數據時會先將數據輸出到緩沖區,待緩沖區充滿后才正式輸出給文件。
三、順序讀寫數據文件
fgetc 函數說明:
作用:從文件指針指向的文件流中讀取一個字符,讀取一個字節后,光標位置后移一個字節
格式:int fgetc(FILE *stream);
返回值:返回所讀取的一個字節,如果讀到文件末尾或者讀取出錯時返回EOF(EOF是文件結束標識符,一般值為-1)
參數說明:
*stream:文件指針,從該文件指針指向的文件中讀取一個字符,然后將光標后移一個字節
fputc 函數說明:
作用:將指定字符寫到文件指針所指向的文件的當前寫指針位置上
格式:int fputc (char c, File *fp);
返回值:在正常調用情況下,函數返回寫入文件的字符的ASCII碼值,出錯時,返回EOF
參數說明:
c:需要寫入的字符
*fp:文件指針,在當前文件指針所指向的文件的當前寫指針位置上寫入一個字符c,然后文件內部寫指針會自動后移一個字節位置
fgets 函數說明:
作用:從文件結構體指針stream中讀取數據,每次讀取一行。讀取的數據保存在buf指向的字符數組中,每次最多讀取bufsize-1個字符(第bufsize個字符賦'\0'),如果文件中的該行,不足bufsize個字符,則讀完該行就結束。如若該行(包括最后一個換行符)的字符數超過bufsize-1,則fgets只返回一個不完整的行,但是,緩沖區總是以NULL字符結尾,對fgets的下一次調用會繼續讀該行。
格式:char *fgets(char *buf, int bufsize, FILE *stream);
返回值:函數成功將返回buf,失敗或讀到文件結尾返回NULL。因此我們不能直接通過fgets的返回值來判斷函數是否是出錯而終止的,應該借助feof函數或者ferror函數來判斷。
參數說明:
*buf: 字符型指針,指向用來存儲所得數據的地址。
bufsize: 整型數據,指明存儲數據的大小。
*stream: 文件指針,將要讀取的文件流。
fputs 函數說明:
作用:向指定的文件寫入一個字符串(不自動寫入字符串結束標記符‘\0’)
格式:int fputs(char *str, FILE *fp);
返回值:若成功返回0,失敗返回EOF
參數說明:
*str: 需要寫入的字符串
*fp: 文件指針,將要寫入的文件流
fprintf 函數說明:
作用:格式化后輸出到文件中
格式:int fprintf (FILE* stream, const char* format, [argument]);
返回值:若成功返回值是輸出的字符數,當發生錯誤時返回一個負值
參數說明:
*stream:文件指針
*format:輸出格式
[argument]:附加參數列表
注:用法與printf函數類似,這里只是參數多了一個文件指針,將格式后的結果輸出到文件中
fscanf 函數說明:
作用:從一個流中執行格式化輸入
格式:int fscanf(FILE* stream,constchar* format,[argument]);
返回值:成功返回讀入的參數的個數,失敗返回EOF。
參數說明:
*stream:文件指針
*format:格式字符串
[argument]:輸入列表
注:用法與scanf函數類似
fread 函數說明:
作用:從一個文件流中讀數據,最多讀取count個元素,每個元素size字節
格式:size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;
返回值:如果調用成功返回實際讀取到的元素個數,如果不成功或讀到文件末尾返回0
參數說明:
*buffer:用於接收數據的內存地址
size:要讀的每個數據項的字節數,單位是字節
count:要讀count個數據項,每個數據項size個字節
*stream:文件指針
注:這個函數以二進制形式對文件進行操作,不局限於文本文件
fwrite 函數說明:
作用:向文件寫入一個數據塊
格式:size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
返回值:返回實際寫入的數據塊數目
參數說明:
*buffer:待寫入的數據的內存地址
size:要寫入內容的單字節數
count:要進行寫入size字節的數據項的個數
*stream:文件指針
注:這個函數以二進制形式對文件進行操作,不局限於文本文件
四、隨機讀寫數據文件
rewind 函數說明:
作用:將文件內部的位置指針重新指向一個流(數據流/文件)的開頭
格式:void rewind(FILE *stream);
返回值:無
參數說明:
*stream:文件指針
ftell 函數說明:
作用:得到文件位置指針當前位置相對於文件首的偏移字節數(測定文件位置標記的當前位置)
格式:long ftell(FILE *stream);
返回值:成功返回當前文件位置,失敗返回-1L
參數說明:
*stream:文件指針
注:因為ftell返回long型,根據long型的取值范圍-2^31~2^31-1(-2147483648~2147483647),故對大於2.1G的文件進行操作時出錯。
fseek 函數說明:
作用:重定位流(數據流/文件)上的文件內部位置指針
格式:int fseek(FILE *stream, long offset, int origin);
返回值:成功返回0,失敗返回非0值
參數說明:
*stream:文件指針
offset:偏移量,正數表示正向偏移,負數表示負向偏移。因為是long型數據,所以應在數字后面加一個字母L
origin:設定從文件的哪里開始偏移,可取值為:SEEK_CUR、 SEEK_END 或 SEEK_SET,詳見下表:
名字 | 起始點 | 用數字代表 |
SEEK_SET | 文件開始位置 | 0 |
SEEK_CUR | 文件當前位置 | 1 |
SEEK_END | 文件末尾位置 | 2 |
示例:
fseek(fp,100L,0); // 把位置指針移動到離文件開頭100字節處 fseek(fp,100L,1); // 把位置指針移動到離文件當前位置100字節處 fseek(fp,-100L,2); // 把位置指針退回到離文件結尾100字節處
五、文件讀寫的出錯檢測
ferror 函數說明:
作用:在調用各種輸入輸出函數(如 putc、getc、fread、fwrite等)時,如果出現錯誤,除了函數返回值有所反映外,還可以用ferror函數檢查
格式:int ferror(FILE *stream);
返回值:返回0表示未出錯,返回非0值表示出錯
參數說明:
*stream:文件指針
clearerr 函數說明:
作用:使文件錯誤標志和文件結束標志置為0。假設在調用一個輸入輸出函數時出現了錯誤,ferror函數值為一個非零值。在調用clearerr(fp)后,ferror(fp)的值變為0。只要出現錯誤標志,就一直保留,直到對同一文件調用clearerr函數或rewind函數,或任何其他一個輸入輸出函數。
格式:void clearerr(FILE *stream);
返回值:無
參數說明:
*stream:文件指針
六、實例:讀取文件A的內容輸出到屏幕上並寫入到文件B中
1、我們先在D盤下新建一個文本文檔(文件名為:A.txt)文件,並在該文件里面寫入任意字符,如果沒有此文件會因為找不到文件而造成文件打開失敗。
2、編譯源代碼並運行程序,程序會將文件("A.txt")里面的字符逐個讀取輸出到屏幕上,並且會在D盤新建一個"B.txt"文件,其內容與"A.txt"文件相同
3、若打開文件失敗,則如下圖所示,此時應檢查文件路徑是否正確(檢查D盤下是否存在"D:\\A.txt"文件)
C語言源代碼:
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 int main() 5 { 6 FILE *fp1, *fp2; // 文件指針 7 char ch; // 用來接收讀取到的字符 8 char filename1[] = "D:\\A.txt"; // 文件A的路徑,需要讀取的文件,如果文件不存在則出錯 9 char filename2[] = "D:\\B.txt"; // 文件B的路徑,將文件A.txt讀取到的數據存入當前文件 10 11 fp1=fopen(filename1, "r"); // 以只讀的方式打開文件A 12 fp2=fopen(filename2, "w"); // 以只寫的方式打開文件B 13 if(fp1==NULL || fp2==NULL) // 判斷文件是否打開成功 14 { //打開文件失敗了 15 printf("cannot open the file!\n"); 16 exit(1); // 退出程序 需要頭文件<stdlib.h> 17 } 18 19 //---開始--讀取文件並輸出到屏幕上---// 20 ch=fgetc(fp1); // 讀取第一個字符 21 while(ch!=EOF) // EOF即文件結束符 22 { 23 putchar(ch); // 將從文件A讀取到的字符輸出到屏幕上 24 ch=fgetc(fp1); // 讀取下一個字符 25 } 26 //---完成--讀取文件並輸出到屏幕上---//
27 28 //---開始--讀取文件A並輸出到文件B里面---// 29 rewind(fp1); // 將文件A的位置指針重新移到文件頭 30 ch=fgetc(fp1); // 讀取第一個字符 31 while(ch!=EOF) // EOF即文件結束符 32 { 33 fputc(ch,fp2); // 將從文件A讀取到的字符寫入到文件B內 34 ch=fgetc(fp1); // 讀取下一個字符 35 } 36 //---完成--讀取文件A並輸出到文件B里面---//
37 38 fclose(fp1); //關閉數據文件 39 fclose(fp2); //關閉數據文件 40 return 0; 41 }