您好,登錄后才能下訂單哦!
這篇文章主要介紹了C語言文件操作實例分析的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇C語言文件操作實例分析文章都會有所收獲,下面我們一起來看看吧。
當我們在編寫一個項目的時候,自然而然想到要把之前寫入的數據保存起來。而只有我們自己選擇刪除數據的時候,數據才不復存在。這就涉及到了數據持久化的問題,我們一般數據持久化的方法有,把數據存放在磁盤文件、存放到數據庫等方式。此處我們就講到如何將數據放入到磁盤文件當中。
磁盤上的文件就是文件。例如電腦當中的C盤內放入的文件夾內的內容就是文件。但是在程序設計中,我們一般談的文件有兩種:程序文件、數據文件(從文件功能的角度來分類的)。
包括源程序文件(后綴為.c),目標文件(windows環境后綴為.obj),可執行程序(windows環境后綴為.exe)。
文件的內容不一定是程序,而是程序運行時讀寫的數據,比如程序運行需要從中讀取數據的文件,或者輸出內容的文件。
此篇博客討論的大部分都是數據文件。因為我們要學會如何將文件中的數據輸入到內存中和如何將程序中的數據輸出到文件當中。在以前各章所處理數據的輸入輸出都是以終端為對象的,即從終端的鍵盤輸入數據,運行結果顯示到顯示器上。其實有時候我們會把信息輸出到磁盤上,當需要的時候再從磁盤上把數據讀取到內存中使用,這里處理的就是磁盤上文件。
一個文件要有一個唯一的文件標識,以便用戶識別和引用。
文件名包含3部分:文件路徑+文件名主干+文件后綴
例如: c:\code\test.txt
為了方便起見,文件標識常被稱為文件名。
緩沖文件系統中,關鍵的概念是“文件類型指針”,簡稱**“文件指針”**。
每個被使用的文件都在內存中開辟了一個相應的文件信息區,用來存放文件的相關信息(如文件的名字,文件狀態及文件當前的位置等)。這些信息是保存在一個結構體變量中的。該結構體類型是有系統聲明的,取名FILE。
例如,VS2013編譯環境提供的 stdio.h 頭文件中有以下的文件類型申明:
struct _iobuf { char *_ptr; int _cnt; char *_base; int _flag; int _file; int _charbuf; int _bufsiz; char *_tmpfname; }; typedef struct _iobuf FILE; FILE* pf;//文件指針變量
不同的C編譯器的FILE類型包含的內容不完全相同,但是大同小異。
每當打開一個文件的時候,系統會根據文件的情況自動創建一個FILE結構的變量,并填充其中的信息,使用者不必關心細節。
一般都是通過一個FILE的指針來維護這個FILE結構的變量,這樣使用起來更加方便。
下面我們可以創建一個FILE*的指針變量:
FILE* pf;//文件指針變量
定義pf是一個指向FILE類型數據的指針變量。可以使pf指向某個文件的文件信息區(是一個結構體變量)。通過該文件信息區中的信息就能夠訪問該文件。也就是說,通過文件指針變量能夠找到與它關聯的文件。
文件在讀寫之前應該先打開文件,在使用結束之后應該關閉文件。
在編寫程序的時候,在打開文件的同時,都會返回一個FILE*的指針變量指向該文件,也相當于建立了指針和文件的關系。
ANSIC 規定使用fopen函數來打開文件,fclose來關閉文件。
要記住的是當打開文件后對數據進行處理完一定要關閉文件,否則可能會造成數據的丟失。
//打開文件 FILE * fopen ( const char * filename, const char * mode ); //關閉文件 int fclose ( FILE * stream );
對于文件的寫入和讀取方式,重點掌握以下幾種即可。
文件使用方式 | 含義 | 如果指定文件不存在 |
---|---|---|
“r”(只讀) | 為了輸入數據,打開一個已經存在的文本文件 | 出錯 |
“w”(只寫) | 為了輸出數據,打開一個文本文件 | 建立一個新的文件 |
“a”(追加) | 向文本文件尾添加數據 | 建立一個新的文件 |
“rb”(只讀) | 為了輸入數據,打開一個二進制文件 | 出錯 |
“wb”(只寫) | 為了輸出數據,打開一個二進制文件 | 建立一個新的文件 |
實例代碼:
/* fopen fclose example */ #include <stdio.h> int main () { FILE * pFile; //打開文件 pFile = fopen ("myfile.txt","w");//以輸出的形式(寫)打開文件 //文件操作 if (pFile!=NULL) { fputs ("fopen example",pFile);//以字符串的形式寫入 //關閉文件 fclose (pFile); } return 0; }
文件的輸出/寫入就是將數據寫入到文件當中,而文件的輸入/讀取就是將文件中的內容讀取到內存當中。
以下的對于文件的讀寫方式的函數均要求掌握
功能 | 函數名 | 適用于 |
---|---|---|
字符輸入函數 | fgetc | 所有輸入流 |
字符輸出函數 | fputc | 所有輸出流 |
文本行輸入函數 | fgets | 所有輸入流 |
文本行輸出函數 | fputs | 所有輸出流 |
格式化輸入函數 | fscanf | 所有輸入流 |
格式化輸出函數 | fprintf | 所有輸出流 |
二進制輸入 | fread | 文件 |
二進制輸出 | fwrite | 文件 |
根據文件指針的位置和偏移量來定位文件指針。文件指針顧名思義也是一個指針,它能指向一個字符串中的某個位置。它要接收的參數有:
第一個參數是文件指針的名字(流),第二個參數是文件指針向后偏移數,第三個參數是fseek函數中規定的三個選項之中的其一。
這三項中第一項是SEEK_CUR,即當前文件指針的偏移處開始向后偏移。第二項是SEEK_END,即從文件的最末尾處開始向前偏移,當然在偏移數一定要為負數才能讀取文件中的內容。第三項是SEEK_SET,即從文件的最前端處開始向后偏移。舉個例子:
#include <stdio.h> int main () { FILE * pFile; pFile = fopen ( "example.txt" , "wb" ); fputs ( "This is an apple." , pFile ); fseek ( pFile , 9 , SEEK_SET ); fputs ( " sam" , pFile ); fclose ( pFile ); return 0; }
為什么最后在記事本中打印出的結果是This is a sample.呢?原因是在第一次fputs中是把This is an apple.先放入記事本當中,當調用fseek函數時,從當前的文件指針處向后偏移9個字節,文件指針一開始默認指向的是文件的首地址處。因此向后偏移9個字節后(偏移一個字節包括空格)指向的是最后一個空格的地址處。而第二次fputs函數是將“ sam”這個內容在上次文件指針指向的地址處開始寫入。因此最后程序運行的結果如圖:
返回文件指針相對于起始位置的偏移量。
這個函數比較簡單,輸入的參數為文件指針流,而返回值的類型為int,即返回的是文件指針所指向的偏移量處。
#include <stdio.h> int main () { FILE * pFile; long size; pFile = fopen ("myfile.txt","rb"); if (pFile==NULL) perror ("Error opening file"); else { fseek (pFile, 0, SEEK_END); // non-portable size=ftell (pFile); fclose (pFile); printf ("Size of myfile.txt: %ld bytes.\n",size); } return 0; }
因為是從文件內容的最末尾處開始相對于起始位置的偏移量。則結果為17。
代碼運行結果為:
讓文件指針的位置回到文件的起始位置。
rewind函數的返回值類型為void型,它所需要的參數是文件指針流。這個函數相對來說也比較簡單,我們直接舉例子。
#include <stdio.h> int main () { int n; FILE * pFile; char buffer [27]; pFile = fopen ("myfile.txt","w+"); for ( n='A' ; n<='Z' ; n++) fputc ( n, pFile); rewind (pFile); fread (buffer,1,26,pFile); fclose (pFile); buffer[26]='\0'; puts (buffer); return 0; }
代碼運行結果:
并且在程序的文件夾中有此內容的記事本產生:
根據數據的組織形式,數據文件被稱為文本文件或者二進制文件。
數據在內存中以二進制的形式存儲,如果不加轉換的輸出到外存,就是二進制文件。
如果要求在外存上以ASCII碼的形式存儲,則需要在存儲前轉換。以ASCII字符的形式存儲的文件就是文本文件。(如整數10000,需要以ASCII碼輸出到磁盤上,則在磁盤中的存儲形式就是10000)。
如有整數10000,如果以ASCII碼的形式輸出到磁盤,則磁盤中占用5個字節(每個字符一個字節),而二進制形式輸出,則在磁盤上只占4個字節(VS2013測試)。
再用整數10000舉例。如果以二進制的形式輸出到磁盤上,則在磁盤上是以二進制的形式存儲。但是我們到文件底下去看二進制形式的文本時,都是亂碼無法看懂(但機器能夠看懂)。此時我們再將該文本文件移到編譯器(VS2019)中。而編譯器內有一個二進制編輯器能夠將該亂碼翻譯為二進制數顯示出來。詳細步驟如下:
代碼:
#include <stdio.h> int main() { int a = 10000; FILE* pf = fopen("test.txt", "wb"); fwrite(&a, 4, 1, pf);//二進制的形式寫到文件中 fclose(pf); pf = NULL; return 0; }
到文件底下去查看文本:
將該文本移到編譯器中后按照以下圖例操作:
此時我們在編譯器中打開該文本:
是什么原因讓10000用二進制的形式存儲變為了10 27 00 00呢?原因是我們先將10000的二進制序列寫出來,為:00000000 00000000 00100111 00010000
,每四位則為一個16進制數字。則結果為00 00 27 10,但是我們的編譯器是以小端的形式存儲的。即數據的低位存儲到內存的低地址中,數據的高位存儲到高地址中。則存儲的形式就為:10 27 00 00 。
在文件讀取過程中,不能用feof函數的返回值直接用來判斷文件的是否結束。
而是應用于當文件讀取結束的時候,判斷是讀取失敗結束,還是遇到文件尾結束。(feof函數是判斷結束過程而不是判斷結束的結果)
1.文本文件讀取是否結束,判斷返回值是否為 EOF(getc)或者NULL(fgets)
例如:
fgetc 判斷是否為 EOF .
fgets 判斷返回值是否為 NULL.
2. 二進制文件的讀取結束判斷,判斷返回值是否小于實際要讀的個數。
例如:
fread判斷返回值是否小于實際要讀的個數。
文件文本中正確使用feof函數的例子:
#include <stdio.h> #include <stdlib.h> int main(void) { int c; // 注意:int,非char,要求處理EOF FILE* fp = fopen("test.txt", "r"); if(!fp) { perror("File opening failed"); return EXIT_FAILURE; } //fgetc 當讀取失敗的時候或者遇到文件結束的時候,都會返回EOF while ((c = fgetc(fp)) != EOF) // 標準C I/O讀取文件循環 { putchar(c); } //判斷是什么原因結束的 if (ferror(fp)) puts("I/O error when reading"); else if (feof(fp)) puts("End of file reached successfully"); fclose(fp); }
二進制文件中正確使用feof函數的例子:
#include <stdio.h> enum { SIZE = 5 }; int main(void) { double a[SIZE] = {1.,2.,3.,4.,5.}; FILE *fp = fopen("test.bin", "wb"); // 必須用二進制模式 fwrite(a, sizeof *a, SIZE, fp); // 寫 double 的數組 fclose(fp); double b[SIZE]; fp = fopen("test.bin","rb"); size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 讀 double 的數組 if(ret_code == SIZE) { puts("Array read successfully, contents: "); for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]); putchar('\n'); } else { // error handling if (feof(fp)) printf("Error reading test.bin: unexpected end of file\n"); else if (ferror(fp)) { perror("Error reading test.bin"); } } fclose(fp); }
說到文件緩沖區,我們就自然而然想到輸入緩沖區,即當一個字符一個字符從鍵盤上輸入時,并不是直接輸入到磁盤內,而是先放到輸入緩沖區,而當輸入緩沖區內的字符放滿后,文件緩沖區才向磁盤內輸入字符。
文件緩沖區也是一樣的道理。從內存向磁盤輸出數據會先送到內存中的緩沖區,裝滿緩沖區后才一起送到磁盤上。如果從磁盤向計算機讀入數據,則從磁盤文件中讀取數據輸入到內存緩沖區(充滿緩沖區),然后再從緩沖區逐個地將數據送到程序數據區(程序變量等)。緩沖區的大小根據C編譯系統決定的。
測試代碼:
#include <stdio.h> #include <windows.h> //VS2013 WIN10環境測試 int main() { FILE*pf = fopen("test.txt", "w"); fputs("abcdef", pf);//先將代碼放在輸出緩沖區 printf("睡眠10秒-已經寫數據了,打開test.txt文件,發現文件沒有內容\n"); Sleep(10000); printf("刷新緩沖區\n"); fflush(pf);//刷新緩沖區時,才將輸出緩沖區的數據寫到文件(磁盤) //注:fflush 在高版本的VS上不能使用了 printf("再睡眠10秒-此時,再次打開test.txt文件,文件有內容了\n"); Sleep(10000); fclose(pf); //注:fclose在關閉文件的時候,也會刷新緩沖區 pf = NULL; return 0; }
我們可以測試一下這個代碼,在程序第一個到fgets函數處時,立刻去打開test.txt文本文件,我們會發現里面沒有內容,而我們用刷新文件緩沖區的fflush函數時再次打開test.txt文本文件時,會發現里面已經有輸入的內容。則能夠證實的確有文件緩沖區的存在。
因為有緩沖區的存在,C語言在操作文件的時候,需要做刷新緩沖區或者在文件操作結束的時候關閉文件。如果不做,可能導致讀寫文件的問題。
關于“C語言文件操作實例分析”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“C語言文件操作實例分析”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。