feof() 與 EOF 是 C 標準庫文件讀取中最容易誤用的概念。本文澄清兩者本質區別,揭示 while(!feof(fp)) 反模式危害,給出正確的讀取循環模式,並探討文本/二進制模式下的行為差異。
<stdio.h>,通常為 -1(int 類型)int 類型變量才能正確存儲 EOF,char 會截斷導致誤判int feof(FILE *stream)while (!feof(fp)) {
char c = fgetc(fp); // 最後一次讀到 EOF 時,feof 尚未置位
process(c); // 處理了無效的 EOF 值(通常是 0xFF 或 -1 截斷)
}
!feof(fp) 為真(標記未置位)fgetc 讀取 'A',返回 'A'!feof(fp) 仍為真(標記仍未置位)fgetc 嘗試讀取,遇到文件結尾,返回 EOF,**並置位 feof 標記**EOF(錯誤!)!feof(fp) 為假,退出結果:多處理了一次無效數據。
int c;
while ((c = fgetc(fp)) != EOF) {
process(c);
}
char buf[1024];
while (fgets(buf, sizeof(buf), fp) != NULL) {
process_line(buf);
}
char buf[4096];
size_t n;
while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
process_block(buf, n);
}
int a, b;
while (fscanf(fp, "%d%d", &a, &b) == 2) { // 返回成功匹配項數
process(a, b);
}
\r\n → \n 轉換(讀時)Ctrl+Z (0x1A) 視為文件結束(歷史遺留)fseek 偏移量不可靠(因行尾轉換)EOF 純粹表示讀取無數據fseek 偏移量精確對應字節位置原文觀點:「說 EOF 只能用於文本文件,其實不然,還要看定義的變量類型。」
EOF 表示結束/錯誤char/unsigned charfread,返回 size_t,不依賴 EOFwhile ((c = fgetc(fp)) != EOF) process(c);
if (ferror(fp)) {
perror("Read error");
clearerr(fp); // 重置錯誤與 EOF 標記,可繼續操作(罕見)
}
| 概念 | 本質 | 正確用法 | 常見誤區 |
|---|---|---|---|
| EOF | int 宏,讀取失敗信號 | 讀取函數返回值直接比較 | 用 char 接收、當作文件結束字符 |
| feof() | 查詢流結束標記(歷史狀態) | 讀取循環結束後區分 EOF/錯誤 | 作為循環條件預測下一次讀取 |
| ferror() | 查詢流錯誤標記 | 讀取結束後檢查 | 忽略錯誤處理 |
牢記原則:**讀取函數的返回值是唯一可靠的循環控制依據**。feof() 只用於循環結束後的事後分析,絕不作為循環守衛。