[英]What are all the reasons `fgetc()` might return `EOF`?
當然fgetc()
在文件結束或發生輸入錯誤時返回EOF
,但這就是全部嗎?這是否意味着沒有更多數據可用?
FILE *inf = ...;
int ch;
while ((ch = fgetc(inf)) != EOF) {
;
}
if (feof(inf)) puts("End-of-file");
else if (ferror(inf)) puts("Error");
else puts("???");
使用feof(), ferror()
進行測試是否足夠?
注意:這里的EOF
是一個計算結果為負數的宏int
,通常是-1
。 它不是end-of-file的同義詞。
這是我可以回答我自己的問題嗎? .
僅此而已,這是否意味着沒有更多可用數據?
不,還有更多EOF
方法。
EOF
並不一定意味着不再有數據 - 這取決於。
C 庫列出了fgetc()
返回EOF
的 3 種情況。
If the end-of-file indicator for the stream is set, or if the stream is at end-of-file, the end-of-file indicator for the stream is set and the
fgetc
function returnsEOF
. 否則,fgetc
function 從 stream 指向的輸入 stream 返回下一個字符。 如果發生讀取錯誤,則設置 stream 的錯誤指示器,並且fgetc
function 返回EOF
。 C17dr § 7.21.7.1 3
回想一下每個stream ,像stdin
一樣,有一個文件結束指示器和錯誤指示器。
stream 剛遇到文件尾
(最常見)已嘗試獲取更多數據,但沒有。
stream 的文件結束指示符已設置
stream 首先檢查其文件結束指示器。 如果它看到指標已設置,則返回EOF
。 沒有嘗試查看是否存在更多數據。 某些類型的流將報告EOF
,但數據將在之前的EOF
報告之后到達。 在與clearerr()
一樣清除文件結束指示符之前,返回保持為EOF
。 示例示例
輸入錯誤
不檢查 stream錯誤指示器。 然而,function 由於某種原因無法讀取文件結尾以外的數據。 一個常見的例子是fputc(stdin)
。 通常輸入錯誤是持久的。 有些不是。 可能有更多數據可用。 常見的策略是結束輸入。
// Example where ferror() is true, yet fgetc() does not return EOF FILE *inf = stdin; printf("end-of-file:%d error:%d\n", feof(inf), ferror(inf)); printf("fputc():%d\n", fputc('?', inf)); // EOF reported printf("end-of-file:%d error:%d\n", feof(inf), ferror(inf)); printf("fgetc():%d\n", fgetc(inf)); // User typed in `A`, 'A' reported printf("end-of-file:%d error:%d\n", feof(inf), ferror(inf));
Output
end-of-file:0 error:0
fputc():-1
end-of-file:0 error:1
fgetc():65
end-of-file:0 error:1
當ferror()
為真時,這並不意味着錯誤剛剛發生,只是在過去的某個時間。
其他案例
由於不正確地保存為char
而導致的明顯EOF
fgetc()
返回一個int
,其值在unsigned char
范圍內, EOF
- 一個負值。 當fgetc()
讀取字符代碼 255,但在char
已簽名的系統上將其保存為char
時,通常會導致char
具有與EOF
相同的值,但不會出現文件結束。
FILE *f = fopen("t", "w"); fputc(EOF & 255, f); fclose(f); f = fopen("t", "r"); char ch = fgetc(f); // Should be int ch printf ("%d %d\n", ch == EOF, ch); printf("end-of-file:%d error:%d\n", feof(f), ferror(f)); fclose(f);
Output
1 -1 // ch == EOF !
end-of-file:0 error:0
UCHAR_MAX == UINT_MAX
的系統。 罕見。
(我只在一些較舊的圖形處理器中遇到過這種情況,仍然是 C 允許的。)在這種情況下, fgetc()
可能會讀取int
范圍之外的unsigned char
,因此在 function 返回時將其轉換為EOF
。 因此fgetc()
返回一個恰好等於EOF
的字符代碼。 這主要是 C 歷史上的一個奇怪現象。 主要處理的一種方法是:
while ((ch = fgetc(inf));= EOF && !feof(inf) && !ferror(inf)) { ; }
很少需要這種迂腐的代碼。
未定義的行為
當然,當 UB 發生時,一切皆有可能。
FILE * f = fopen("Some_non_existent_file", "r"); // Should have tested f == NULL here printf("%d\n", fgetc(f) == EOF); // Result may be 1
一種處理來自fgetc()
的返回的可靠方法。
FILE *inf = ...;
if (inf) { // Add test
int ch; // USE int !
// Pedantic considerations, usually can be ignored
#if UCHAR_MAX > INT_MAX
clearerr(inf); // Clear history of prior flags
while ((ch = fgetc(inf)) != EOF && !feof(inf) && !ferror(inf)) {
;
}
#else
while ((ch = fgetc(inf)) != EOF) {
;
}
#endif
if (feof(inf)) puts("End-of-file");
else puts("Error");
如果代碼需要在end-of-file或error之后查找數據,請調用clearerr()
並重復if()
塊。
EOF 不一定意味着“沒有更多數據”的另一種情況是(而不是“是”)讀取磁帶。 一個磁帶上可以有多個文件,每個文件的結尾都標有 EOF。 當您遇到 EOF 時,您使用clearerr(fp)
重置文件 stream 上的 EOF 和錯誤狀態,然后您可以繼續讀取磁帶上的下一個文件。 然而,磁帶(在大多數情況下)已經走上了渡渡鳥的道路,所以這幾乎不再重要了。
這是一個晦澀的原因:
在0x1A
上,以文本模式讀取字節 0x1A 會導致 EOF。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.