文章目錄
- 一、為什么需要文件操作?
- 二、認識文件:不止是磁盤上的存儲
- 2.1 程序文件
- 2.2 數據文件
- 2.3 文件名的構成
- 三、文本文件與二進制文件:數據的兩種形態
- 3.1 存儲方式差異
- 3.2 實例對比:整數10000的存儲
- 3.3 二進制文件操作示例
- 四、文件的打開與關閉:操作的開始與結束
- 4.1 流的概念
- 4.2 文件指針
- 4.3 打開與關閉函數
- 4.4 文件打開模式
- 4.5 示例代碼
- 五、文件的順序讀寫:按部就班的數據處理
- 5.1 常用順序讀寫函數
- 5.2 函數對比
- 六、文件的隨機讀寫:自由定位數據位置
- 6.1 fseek函數:定位文件指針
- 6.2 ftell函數:獲取當前位置偏移量
- 6.3 rewind函數:重置文件指針
- 七、文件讀取結束的判定:正確識別結束條件
- 7.1 feof函數的正確用法
- 7.2 正確的判斷方式
- 八、文件緩沖區:提升效率的中間層
- 8.1 緩沖區演示
- 8.2 重要結論
- 總結
在C語言編程中,文件操作是實現數據持久化存儲的關鍵技術。無論是保存用戶數據、配置信息還是處理大量數據,都離不開文件操作。本文將詳細講解C語言文件操作的方方面面,從基本概念到實際應用,幫助你全面掌握這一重要技能。
一、為什么需要文件操作?
在程序運行過程中,我們處理的數據通常存儲在內存中。然而,內存具有臨時性的特點——當程序退出或計算機斷電時,內存中的數據會全部丟失。如果我們希望數據能夠長期保存,以便下次運行程序時繼續使用,就必須使用文件將數據存儲到磁盤等外部存儲設備中。
簡單來說,文件操作讓程序的數據擁有了"記憶"能力,是實現數據持久化的核心手段。
二、認識文件:不止是磁盤上的存儲
在C語言中,我們通常從功能角度將文件分為兩類:
2.1 程序文件
程序文件是用于構成程序本身的文件,包括:
- 源程序文件(.c后綴)
- 目標文件(Windows環境下為.obj后綴)
- 可執行程序(Windows環境下為.exe后綴)
2.2 數據文件
數據文件是程序運行時讀寫的數據載體,比如:
- 程序需要讀取的配置文件
- 程序輸出的日志文件
- 存儲用戶信息的數據庫文件
本文重點討論的是數據文件的操作。
2.3 文件名的構成
一個完整的文件名包含三個部分:
- 文件路徑:指示文件在磁盤中的位置
- 文件名主干:文件的核心標識
- 文件后綴:指示文件類型
例如:c:\code\test.txt
中,c:\code\
是路徑,test
是文件名主干,.txt
是后綴。
三、文本文件與二進制文件:數據的兩種形態
根據數據的存儲形式,數據文件可分為文本文件和二進制文件,它們的區別如下:
3.1 存儲方式差異
- 二進制文件:數據在內存中以二進制形式存儲,不加轉換直接輸出到外存
- 文本文件:數據在存儲前被轉換為ASCII碼形式,以字符形式存儲
3.2 實例對比:整數10000的存儲
以整數10000為例:
- 文本文件存儲:占用5個字節(每個字符一個字節),存儲內容是’1’,‘0’,‘0’,‘0’,'0’的ASCII碼
- 二進制文件存儲:在VS2019環境下僅占用4個字節,直接存儲其二進制形式
00000000 00000000 00100111 00010000
3.3 二進制文件操作示例
#include <stdio.h>
int main()
{int a = 10000;FILE* pf = fopen("test.txt", "wb"); // 以二進制寫入模式打開文件fwrite(&a, 4, 1, pf); // 將a以二進制形式寫入文件fclose(pf);pf = NULL;return 0;
}
在VS中查看二進制文件時,需要使用二進制編輯器,10000會顯示為10270000
。
四、文件的打開與關閉:操作的開始與結束
4.1 流的概念
C語言通過"流"(stream)來操作各種外部設備(包括文件)。流可以想象成"流淌著字符的河",程序通過流與外部設備進行數據交換。
C程序啟動時會默認打開3個標準流:
stdin
:標準輸入流(通常對應鍵盤)stdout
:標準輸出流(通常對應顯示器)stderr
:標準錯誤流(通常對應顯示器)
這就是為什么我們可以直接使用scanf
和printf
函數進行輸入輸出,而無需手動打開流。
4.2 文件指針
緩沖文件系統中,通過文件指針(FILE*
類型)來管理文件。每個被打開的文件在內存中都有一個對應的文件信息區(結構體),存儲文件名、狀態、當前位置等信息,文件指針就指向這個結構體。
定義文件指針的方式:
FILE* pf; // 聲明一個文件指針變量
4.3 打開與關閉函數
-
打開文件:使用
fopen
函數FILE * fopen ( const char * filename, const char * mode );
-
關閉文件:使用
fclose
函數int fclose ( FILE * stream );
4.4 文件打開模式
文件打開模式決定了對文件的操作權限,常見模式如下:
文件使用方式 | 含義 | 如果指定文件不存在 |
---|---|---|
“r”(只讀) | 打開文本文件用于輸入 | 出錯 |
“w”(只寫) | 打開文本文件用于輸出 | 創建新文件 |
“a”(追加) | 向文本文件末尾添加數據 | 創建新文件 |
“rb”(只讀) | 打開二進制文件用于輸入 | 出錯 |
“wb”(只寫) | 打開二進制文件用于輸出 | 創建新文件 |
“ab”(追加) | 向二進制文件末尾添加數據 | 創建新文件 |
“r+”(讀寫) | 打開文本文件用于讀寫 | 出錯 |
“w+”(讀寫) | 創建文本文件用于讀寫 | 創建新文件 |
“a+”(讀寫) | 打開文本文件用于讀寫,從末尾開始 | 創建新文件 |
4.5 示例代碼
#include <stdio.h>
int main ()
{FILE * pFile;// 打開文件pFile = fopen ("myfile.txt","w");// 文件操作if (pFile!=NULL){fputs ("fopen example",pFile);// 關閉文件fclose (pFile);}return 0;
}
五、文件的順序讀寫:按部就班的數據處理
順序讀寫是指從文件的開頭到結尾依次進行讀寫操作,C語言提供了一系列函數實現這一功能:
5.1 常用順序讀寫函數
函數名 | 功能 | 適用于 |
---|---|---|
fgetc | 字符輸入函數 | 所有輸入流 |
fputc | 字符輸出函數 | 所有輸出流 |
fgets | 文本行輸入函數 | 所有輸入流 |
fputs | 文本行輸出函數 | 所有輸出流 |
fscanf | 格式化輸入函數 | 所有輸入流 |
fprintf | 格式化輸出函數 | 所有輸出流 |
fread | 二進制輸入 | 文件 |
fwrite | 二進制輸出 | 文件 |
5.2 函數對比
-
輸入函數家族:
scanf
:從標準輸入流讀取格式化數據fscanf
:從指定輸入流讀取格式化數據sscanf
:從字符串讀取格式化數據
-
輸出函數家族:
printf
:向標準輸出流輸出格式化數據fprintf
:向指定輸出流輸出格式化數據sprintf
:將格式化數據輸出到字符串
六、文件的隨機讀寫:自由定位數據位置
隨機讀寫允許程序直接定位到文件的任意位置進行操作,主要通過以下函數實現:
6.1 fseek函數:定位文件指針
根據文件指針的當前位置和偏移量來定位指針:
int fseek ( FILE * stream, long int offset, int origin );
參數origin
可以是:
SEEK_SET
:從文件開頭開始計算SEEK_CUR
:從當前位置開始計算SEEK_END
:從文件末尾開始計算
示例:
#include <stdio.h>
int main ()
{FILE * pFile;pFile = fopen ( "example.txt" , "wb" );fputs ( "This is an apple." , pFile );fseek ( pFile , 9 , SEEK_SET ); // 定位到第10個字符位置fputs ( " sam" , pFile ); // 從該位置開始寫入fclose ( pFile );return 0;
}
// 最終文件內容為:"This is a sample."
6.2 ftell函數:獲取當前位置偏移量
返回文件指針相對于文件起始位置的偏移量:
long int ftell ( FILE * stream );
示例:計算文件大小
#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); // 定位到文件末尾size=ftell (pFile); // 獲取偏移量,即文件大小fclose (pFile);printf ("Size of myfile.txt: %ld bytes.\n",size);}return 0;
}
6.3 rewind函數:重置文件指針
將文件指針重新定位到文件開頭:
void rewind ( FILE * stream );
示例:
#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';printf(buffer); // 輸出ABCDEFGHIJKLMNOPQRSTUVWXYZreturn 0;
}
七、文件讀取結束的判定:正確識別結束條件
很多初學者會錯誤地使用feof
函數來判斷文件是否結束,這是需要避免的。
7.1 feof函數的正確用法
feof
函數的作用是:當文件讀取結束時,判斷結束的原因是否是"遇到文件尾"。它并不能直接用來判斷文件是否結束。
7.2 正確的判斷方式
-
文本文件:
- 使用
fgetc
時,判斷返回值是否為EOF
- 使用
fgets
時,判斷返回值是否為NULL
示例:
#include <stdio.h> #include <stdlib.h> int main(void) {int c; // 注意:必須是int類型,以處理EOFFILE* fp = fopen("test.txt", "r");if(!fp) {perror("File opening failed");return EXIT_FAILURE;}// fgetc讀取失敗或遇到文件結束時返回EOFwhile ((c = fgetc(fp)) != EOF)putchar(c);// 判斷結束原因if (ferror(fp))puts("I/O error when reading");else if (feof(fp))puts("End of file reached successfully");fclose(fp); }
- 使用
-
二進制文件:
- 使用
fread
時,判斷返回值是否小于實際要讀取的個數
示例:
#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); // 寫入數組fclose(fp);double b[SIZE];fp = fopen("test.bin","rb");size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 讀取數組if(ret_code == SIZE) {puts("Array read successfully, contents: ");for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);putchar('\n');} else { // 錯誤處理if (feof(fp))printf("Error reading test.bin: unexpected end of file\n");else if (ferror(fp)) {perror("Error reading test.bin");}}fclose(fp); }
- 使用
八、文件緩沖區:提升效率的中間層
ANSI C標準采用"緩沖文件系統",系統會自動為每個正在使用的文件在內存中開辟一塊"文件緩沖區":
- 輸出數據時:先送到內存緩沖區,緩沖區滿后再一起寫入磁盤
- 輸入數據時:先從磁盤讀取到緩沖區,再從緩沖區送到程序數據區
緩沖區的大小由C編譯系統決定。
8.1 緩沖區演示
#include <stdio.h>
#include <windows.h> // VS2019 WIN11環境
int main()
{FILE* pf = fopen("test.txt", "w");fputs("abcdef", pf); // 數據先存入輸出緩沖區printf("睡眠10秒-此時打開test.txt,文件無內容\n");Sleep(10000);printf("刷新緩沖區\n");fflush(pf); // 手動刷新緩沖區,數據寫入磁盤printf("再睡眠10秒-此時打開test.txt,文件有內容\n");Sleep(10000);fclose(pf); // 關閉文件時也會刷新緩沖區pf = NULL;return 0;
}
8.2 重要結論
由于緩沖區的存在,操作文件時必須注意:
- 及時刷新緩沖區(使用
fflush
函數) - 操作結束后關閉文件(
fclose
會自動刷新緩沖區)
否則可能導致數據未能正確寫入文件,造成數據丟失或不一致。
總結
文件操作是C語言編程中的重要技能,掌握它可以讓你的程序具備數據持久化能力。本文介紹了文件的基本概念、類型劃分、打開關閉、順序讀寫、隨機讀寫、結束判定以及緩沖區機制等內容。
在實際編程中,要注意以下幾點:
- 始終檢查文件是否成功打開
- 操作完成后及時關閉文件
- 正確判斷文件讀取結束的條件
- 理解并合理利用緩沖區機制