在實際應用中,我們往往需要對文件進行操作,下面我將介紹C語言的一些關於操作文件的函數。
一、計算機文件
計算機文件是以計算機硬盤為載體存儲在計算機上的信息集合,是存儲在某種長期儲存設備上的一段數據流。在C語言中用一個指針變量指向一個文件,這個指針稱為文件指針。通過文件指針就可對它所指的文件進行各種操作,C語言規定該文件指針類型為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 * fopen(const char * path,const char * mode);
返回值:打開文件成功返回一個文件指針,若打開文件失敗則返回NULL
參數說明:
*path:字符串包含欲打開的文件路徑及文件名(例如:D:\\A.txt),注意在C語言中欲輸出一個"\"則需要輸兩個"\\"
*mode:代表着流形態,mode有下列幾種形態字符串:
r 打開只讀文件,該文件必須存在。
r+ 打開可讀寫的文件,該文件必須存在。
rb+ 讀寫打開一個二進制文件,只允許讀寫數據。
rt+ 讀寫打開一個文本文件,允許讀和寫。
w 打開只寫文件,若文件存在則文件長度清為0,即該文件內容會消失。若文件不存在則建立該文件。
w+ 打開可讀寫文件,若文件存在則文件長度清為零,即該文件內容會消失。若文件不存在則建立該文件。
a 以附加的方式打開只寫文件。若文件不存在,則會建立該文件,如果文件存在,寫入的數據會被加到文件尾,即文件原先的內容會被保留。(EOF符保留)
a+ 以附加方式打開可讀寫的文件。若文件不存在,則會建立該文件,如果文件存在,寫入的數據會被加到文件尾后,即文件原先的內容會被保留。(原來的EOF符不保留)
wb 只寫打開或新建一個二進制文件;只允許寫數據。
wb+ 讀寫打開或建立一個二進制文件,允許讀和寫。
wt+ 讀寫打開或着建立一個文本文件;允許讀寫。
at+ 讀寫打開一個文本文件,允許讀或在文本末追加數據。
ab+ 讀寫打開一個二進制文件,允許讀或在文件末追加數據。
上述的形態字符串都可以再加一個b字符,如rb、w+b或ab+等組合,加入b 字符用來告訴函數庫打開的文件為二進制文件,而非純文字文件。
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 }