一、FILE 結構體的本質與定義
-
基本概念
FILE
是 C 語言標準庫中用于封裝文件操作的結構體類型,定義于<stdio.h>
中。它代表一個“文件流”,可以是磁盤文件、標準輸入輸出(stdin/stdout/stderr)或其他輸入輸出設備。 -
實現特性
- 具體成員由編譯器實現決定(如 GCC、Clang、MSVC 可能不同),不可直接訪問內部字段,必須通過標準庫函數操作。
- 包含文件句柄、緩沖區、狀態標志、位置指針等關鍵信息。
二、FILE 結構體的核心成員(抽象功能描述)
雖然具體成員不透明,但可歸納其核心功能模塊:
-
文件標識與連接
- 文件描述符(如 Unix 的
int fd
,Windows 的HANDLE
):底層系統用于標識文件的句柄。 - 打開模式:記錄文件以讀、寫、追加、文本/二進制模式打開的狀態(如
r
,w+
,ab
等)。
- 文件描述符(如 Unix 的
-
緩沖區管理
- 緩沖區指針:指向用于暫存數據的內存區域(如
char* buffer
)。 - 緩沖區大小:緩沖區的容量(如
size_t buffer_size
)。 - 當前緩沖區位置:記錄已使用的緩沖區長度(如
size_t cur_pos
)。 - 緩沖區類型:全緩沖(默認文件)、行緩沖(stdout)、無緩沖(stderr),可通過
setvbuf
配置。
- 緩沖區指針:指向用于暫存數據的內存區域(如
-
文件位置與偏移
- 位置指針:記錄當前讀寫位置(二進制文件為字節偏移,文本文件可能涉及換行符轉換后的邏輯位置)。
long int pos
(或類似成員):通過ftell
/fseek
操作的底層位置。
-
狀態標志
- 錯誤標志(
ferror
):文件操作出錯時置位(如磁盤損壞、權限不足)。 - EOF 標志(
feof
):文件讀取到末尾時置位。 - 打開狀態:標記文件是否已關閉(避免重復關閉導致錯誤)。
- 錯誤標志(
-
寬字符與本地化
- 寬字符流(C99 引入):若處理寬字符(如
wchar_t
),包含額外的寬字符緩沖區和轉換狀態(如FILEW
,C11 合并為FILE
支持寬字符)。
- 寬字符流(C99 引入):若處理寬字符(如
三、文件流的打開與關閉
-
打開文件:
fopen
與模式字符串- 原型:
FILE* fopen(const char* filename, const char* mode);
- 模式說明:
- 基礎模式:
r
(讀,不存在則失敗)、w
(寫,清空或創建)、a
(追加,不存在則創建)。 - 二進制模式:追加
b
(如rb
,wb+
),避免文本模式的換行符轉換(Windows 下\r\n
?\n
)。 - 更新模式:追加
+
(如r+
可讀可寫,不允許同時讀寫同一位置未刷新)。
- 基礎模式:
- 返回值:成功返回
FILE*
,失敗返回NULL
(需檢查!)。
- 原型:
-
關閉文件:
fclose
- 作用:刷新緩沖區(未寫入的數據強制寫入磁盤)、釋放資源、關閉底層文件句柄。
- 返回值:成功返回
0
,失敗返回EOF
(如磁盤已滿、文件被刪除)。 - 注意:程序結束時自動關閉所有打開的文件流,但顯式調用
fclose
是良好習慣。
四、文件讀寫操作與緩沖區機制
-
字符級操作
- 讀:
int fgetc(FILE* stream)
(讀單個字符,返回unsigned char
轉換為int
,EOF 時返回EOF
)。 - 寫:
int fputc(int c, FILE* stream)
(寫單個字符,成功返回c
,失敗返回EOF
)。
- 讀:
-
行/字符串操作
- 讀:
char* fgets(char* s, int size, FILE* stream)
(讀取一行或size-1
個字符,包含\n
,末尾補\0
)。 - 寫:
int fputs(const char* s, FILE* stream)
(寫入字符串,不包含末尾\0
)。
- 讀:
-
塊讀寫(二進制文件)
- 原型:
size_t fread(void* ptr, size_t size, size_t count, FILE* stream);
size_t fwrite(const void* ptr, size_t size, size_t count, FILE* stream);
- 作用:按塊讀取/寫入數據,
size*count
為總字節數,返回實際操作的完整塊數(可能小于count
因錯誤或 EOF)。
- 原型:
-
格式化讀寫
- 讀:
int fscanf(FILE* stream, const char* format, ...);
(按格式解析輸入,返回成功匹配的參數數)。 - 寫:
int fprintf(FILE* stream, const char* format, ...);
(按格式生成輸出,返回實際寫入的字符數)。
- 讀:
-
緩沖區控制
- 自動緩沖:標準庫根據流類型自動選擇緩沖策略(文件默認全緩沖,終端行緩沖,stderr 無緩沖)。
- 手動配置:
int setvbuf(FILE* stream, char* buffer, int mode, size_t size);
mode
:_IOFBF
(全緩沖)、_IOLBF
(行緩沖)、_IONBF
(無緩沖)。
- 強制刷新:
int fflush(FILE* stream)
(刷新緩沖區,對讀流無意義,stream=NULL
時刷新所有輸出流)。
五、文件定位與隨機訪問
-
絕對定位
int fseek(FILE* stream, long offset, int origin);
origin
:SEEK_SET
(文件開頭)、SEEK_CUR
(當前位置)、SEEK_END
(文件末尾)。- 文本文件限制:
offset
必須是之前ftell
的返回值(因換行符轉換可能導致邏輯與物理位置不一致)。
-
相對定位
void rewind(FILE* stream);
(將位置重置為開頭,清除錯誤和 EOF 標志)。
-
獲取當前位置
long ftell(FILE* stream);
(返回當前位置,文本文件可能不精確,需配合fseek
使用)。int fgetpos(FILE* stream, fpos_t* pos);
和int fsetpos(FILE* stream, const fpos_t* pos);
(更精確的定位,支持大文件)。
六、錯誤處理與狀態檢查
-
錯誤標志
int ferror(FILE* stream);
(非零表示有錯誤,需在操作后立即檢查)。void clearerr(FILE* stream);
(清除錯誤和 EOF 標志)。
-
EOF 檢測
int feof(FILE* stream);
(僅在讀取操作失敗后為真,避免提前判斷while(!feof(stream))
導致多讀一次)。
七、標準流與特殊文件流
-
預定義的標準流
stdin
(標準輸入,對應鍵盤,默認打開,r
模式)。stdout
(標準輸出,對應屏幕,默認打開,w
模式,行緩沖)。stderr
(標準錯誤,對應屏幕,默認打開,w
模式,無緩沖,錯誤信息即時輸出)。
-
臨時文件
FILE* tmpfile(void);
(創建臨時二進制文件,關閉或程序結束時自動刪除)。char* tmpnam(char* s);
(生成唯一的臨時文件名,避免沖突)。
八、高級特性與注意事項
-
二進制 vs 文本模式
- 文本模式:自動轉換換行符(如 Windows 下寫入
\n
轉為\r\n
,讀取時反轉),可能導致文件大小變化。 - 二進制模式:原樣讀寫字節,適用于圖片、可執行文件等,避免換行符干擾。
- 文本模式:自動轉換換行符(如 Windows 下寫入
-
寬字符流
- C99 引入寬字符函數(如
fgetwc
,fputwc
,fwprintf
),通過fopen
的模式L
(如L"rb"
)打開寬字符流,處理wchar_t
數據。
- C99 引入寬字符函數(如
-
多字節流與本地化
fgetc
/fputc
處理單字節字符,fgetws
/fputws
處理寬字符,依賴本地化環境(setlocale
)。
-
線程安全
- 標準 IO 函數通常是線程安全的,但多個線程同時操作同一
FILE
流可能導致緩沖區競爭(建議加鎖或使用獨立流)。
- 標準 IO 函數通常是線程安全的,但多個線程同時操作同一
-
常見陷阱
- 未檢查
fopen
返回值導致空指針解引用。 - 文本模式下對二進制文件操作導致數據損壞(如
\r
被過濾)。 - 忘記刷新緩沖區(如程序崩潰前未
fflush
或fclose
,導致數據丟失)。 fgets
未指定緩沖區大小導致溢出(必須傳入size
參數)。
- 未檢查
九、總結
FILE
結構體是 C 語言文件 IO 的核心,通過標準庫函數間接操作,涵蓋以下核心知識:
- 文件打開與關閉:模式字符串、錯誤檢查、資源釋放。
- 讀寫操作:字符、行、塊、格式化,緩沖區機制。
- 定位與狀態:位置指針、錯誤/EOF 標志、緩沖控制。
- 特殊流與高級特性:標準流、臨時文件、二進制/文本模式、寬字符支持。
- 最佳實踐:錯誤處理、避免緩沖區溢出、合理使用緩沖策略。