EOF是什么?(转)
我學習C語言的時候,遇到的一個問題就是EOF。
它是end of file的縮寫,表示"文字流"(stream)的結尾。這里的"文字流",可以是文件(file),也可以是標準輸入(stdin)。
比如,下面這段代碼就表示,如果不是文件結尾,就把文件的內容復制到屏幕上。
int c;
while ((c = fgetc(fp)) != EOF) {
putchar (c);
}
很自然地,我就以為,每個文件的結尾處,有一個叫做EOF的特殊字符,讀取到這個字符,操作系統就認為文件結束了。
但是,后來我發現,EOF不是特殊字符,而是一個定義在頭文件stdio.h的常量,一般等于-1。
#define EOF (-1)
于是,我就困惑了。
如果EOF是一個特殊字符,那么假定每個文本文件的結尾都有一個EOF(也就是-1),還是可以做到的,因為文本對應的ASCII碼都是正值,不可能有負值。但是,二進制文件怎么辦呢?怎么處理文件內部包含的-1呢?
這個問題讓我想了很久,后來查了資料才知道,在Linux系統之中,EOF根本不是一個字符,而是當系統讀取到文件結尾,所返回的一個信號值(也就是-1)。至于系統怎么知道文件的結尾,資料上說是通過比較文件的長度。
所以,處理文件可以寫成下面這樣:
int c;
while ((c = fgetc(fp)) != EOF) {
do something
}
這樣寫有一個問題。fgetc()不僅是遇到文件結尾時返回EOF,而且當發生錯誤時,也會返回EOF。因此,C語言又提供了feof()函數,用來保證確實是到了文件結尾。上面的代碼feof()版本的寫法就是:
int c;
while (!feof(fp)) {
c = fgetc(fp);
do something;
}
但是,這樣寫也有問題。fgetc()讀取文件的最后一個字符以后,C語言的feof()函數依然返回0,表明沒有到達文件結尾;只有當fgetc()向后再讀取一個字符(即越過最后一個字符),feof()才會返回一個非零值,表示到達文件結尾。
所以,按照上面這樣寫法,如果一個文件含有n個字符,那么while循環的內部操作會運行n+1次。所以,最保險的寫法是像下面這樣:
int c = fgetc(fp);
while (c != EOF) {
do something;
c = fgetc(fp);
}
if (feof(fp)) {
printf("\n End of file reached.");
} else {
printf("\n Something went wrong.");
}
除了表示文件結尾,EOF還可以表示標準輸入的結尾。
int c;
while ((c = getchar()) != EOF) {
putchar(c);
}
但是,標準輸入與文件不一樣,無法事先知道輸入的長度,必須手動輸入一個字符,表示到達EOF。
Linux中,在新的一行的開頭,按下Ctrl-D,就代表EOF(如果在一行的中間按下Ctrl-D,則表示輸出"標準輸入"的緩存區,所以這時必須按兩次Ctrl-D);Windows中,Ctrl-Z表示EOF。(順便提一句,Linux中按下Ctrl-Z,表示將該進程中斷,在后臺掛起,用fg命令可以重新切回到前臺;按下Ctrl-C表示終止該進程。)
那么,如果真的想輸入Ctrl-D怎么辦?這時必須先按下Ctrl-V,然后就可以輸入Ctrl-D,系統就不會認為這是EOF信號。Ctrl-V表示按"字面含義"解讀下一個輸入,要是想按"字面含義"輸入Ctrl-V,連續輸入兩次就行了。
留言(22條)
在Linux系統之中,EOF根本不是一個字符,而是當系統讀取到文件結
尾,所返回的一個信號值(也就是-1)
int c;
while ((c = fgetc(fp)) != EOF)
------------
有個問題請教一下,如果c = fgetc(fp) 讀到的是二進制的-1,怎么區分讀到的c(-1)跟信號值(-1)呢?
2011年11月12日 21:00?|?檔案?|?引用
@wdd:
我的理解是這樣的,不一定對,歡迎討論。
根據函數定義,除了EOF,fgetc()函數返回的都是unsigned char類型,而c是int類型,也就是說,這里發生了類型轉換。除了最后一個字節,c的前幾個字節肯定都是0。所以,除非出現EOF,否則c肯定是一個正值,不可能等于-1。
2011年11月12日 21:19?|?檔案?|?引用
你輸入-1實際上是‘-’ ‘1’。除非你真用二進制讀寫函數讀寫文件那就不存在EOF而是按字節塊讀寫,fgetc是文本型處理文件的,不存在讀2進制的-1,一次只處理一個字節,這里有個重要概念,文件分為:文本視圖,2進制視圖。打開文件分文本模式打開,2進制打開。處理也分文本模式和2進制模式。所以不要混淆
2011年11月12日 22:25?|?檔案?|?引用
補充一點,Linux和Windows的輸入分別是采用非阻塞式和阻塞式,也就是說Linux中你按下Ctrl+D后,程序立即響應,將鍵盤輸入的東西放到輸入緩沖區中(同時忽略輸入的這個Ctrl+D),而Windows下則是你按了回車以后它才去看你輸入有沒有Ctrl+Z的
2011年11月12日 22:27?|?檔案?|?引用
對了,至于那個-1,請務必好好研讀下ASC II,他是7位0~127個字符,第8位是沒有用的,所以signed char中如果第8位為1,表示負數也好正數也好,在ASC表里都不對應任何字符
2011年11月12日 22:34?|?檔案?|?引用
int
fgetc(FILE *fp)
{
return (__sgetc(fp));
}
google codesearch搜到的代碼是這樣的,返回值是int,不是unsigned int
2011年11月13日 07:14?|?檔案?|?引用
引用阮一峰的發言:
根據函數定義,除了EOF,fgetc()函數返回的都是unsigned char類型,而c是int類型,也就是說,這里發生了類型轉換。除了最后一個字節,c的前幾個字節肯定都是0。所以,除非出現EOF,否則c肯定是一個正值,不可能等于-1。
這里的思路是對的,但說法上有問題。
在阮一峰先生的那篇推薦C語言教材的文章中,我說過,阮先生該看一些基礎性的文檔。C 語言中,函數的類型是由函數聲明確定的,參數和返回值的類型都不能變。您看 K&R 有困惑,說明您和其它學習者交流不足,有些課堂上接觸的基礎概念您不熟悉。
C 語言標準庫中,處理單個字符的函數一般都是將字符當作 int 型處理的,所以不存在什么類型轉換,直接就可以將 EOF 和 char 返回為不沖突的二進制表達形式。
2011年11月13日 08:04?|?檔案?|?引用
引用iSayme的發言:
google codesearch搜到的代碼是這樣的,返回值是int,不是unsigned int
fgetc要返回EOF,返回值的類型肯定是int啊。
我是說,除了EOF,其他返回值都是unsigned char。
2011年11月13日 09:54?|?檔案?|?引用
引用wdd的發言:
在Linux系統之中,EOF根本不是一個字符,而是當系統讀取到文件結
尾,所返回的一個信號值(也就是-1)
int c;
while ((c = fgetc(fp)) != EOF)
------------
有個問題請教一下,如果c = fgetc(fp) 讀到的是二進制的-1,怎么區分讀到的c(-1)跟信號值(-1)呢?
可以區分的,原因:32位系統下,int是4字節的,-1對應的是0xFFFFFFFF,而fgetc()是一個個字節地讀,所以,如果有一個二進制文件的4個字節是-1,那么實際上會讀出4個0xFF來。
2011年11月13日 11:07?|?檔案?|?引用
引用iSayme的發言:
int
fgetc(FILE *fp)
{
return (__sgetc(fp));
}
google codesearch搜到的代碼是這樣的,返回值是int,不是unsigned int
2011年11月13日 20:41?|?檔案?|?引用
不錯,既懂程序又懂英語,人才。
2011年11月13日 22:13?|?檔案?|?引用
引用Dan的發言:
這個要看編譯器。。。 每個編譯器規范不一樣,ANSI C不規定是signed 還是 unsigned ,char本身就是用整形實現的
?
其實 C99 里的7.19.7.1小節(n1256.pdf 的第308頁)規定了 fgetc 要按照 unsigned char 來讀文件中的字節,并轉為 int 型返回。
2011年11月14日 06:28?|?檔案?|?引用
雖然是一個簡單的問題,但看你娓娓道來十分有趣。
2011年11月14日 09:15?|?檔案?|?引用
應該是這樣吧:
首先肯定的是返回值都是int型的。
如果讀二進制讀到-1,則其實讀到的是0xff,fgetc返回時將其轉為int型即0x00ff,對于int型來說,0x00ff不等于-1;
如果fgetc返回值是-1,則說明返回的是int型的-1即0xffff;
2011年11月14日 10:04?|?檔案?|?引用
阮兄,您的rss在谷歌reader上總是報錯啊
2011年11月14日 11:46?|?檔案?|?引用
受教了!! 謝謝你!! ^^b
2011年11月14日 11:53?|?檔案?|?引用
俺以前一直都是不求甚解,看到大伙討論,man了一下fgetc:
fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.
確實是無符號字符的方式;
另外,fgetc只是取流的內容的接口,與流的實際存在形式是分離的,因此設定一些實現無關的標識來標明特殊情況也是合理的。
2011年11月14日 13:32?|?檔案?|?引用
謝謝分享。一直都不太懂eof,現在廓然開朗。
2011年11月14日 13:46?|?檔案?|?引用
fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.
這兩點肯定是對的!
另外在K&R書中,提到一個經典的bug:
char c;
while((c = fgetc(stdin)) != EOF)
putchar(c);
......
fgetc(stdin)返回的一個unsigned char然后轉換成int,然后在賦值運算:
c = fgetc(stdin)中又轉換為char。最后在“!=”運算符中又要從char轉換到int。
那么:(int)0xff ?= (EOF = 0xffff)呢?
如果系統的char是默認是signed char,那么沒有問題。因為(int)((signed char)0xff) == (EOF = 0xffff),可以正確的表示到達了文件(輸入)末尾的概念。
但如果系統的char是默認是unsigned char,那么就有問題了。因為(int)((unsigned char)0xff != (EOF = 0xffff)。這樣就沒有辦法正確的表達文件(輸入)到達末尾的概念。這樣就成了一個死循環。
2011年11月18日 13:16?|?檔案?|?引用
給出一段嚴重代碼:
給出一段驗證代碼:
#include?
int main()
{
signed char sc = 0xff;
unsigned char uc = 0xff;
int i = sc;
int j = uc;
int eof = -1; // END OF FILE
printf("i = %d = 0x%x\n", i, i);
printf("j = %d = 0x%x\n", j, j);
printf("sc == eof: %d\n", (sc == eof));
printf("uc == eof: %d\n", (uc == eof));
return 0;
}
運行結果:
digdeep@ubuntu:~/uulp$ ./char
i = -1 = 0xffffffff
j = 255 = 0xff
sc == eof: 1
uc == eof: 0
2011年11月18日 13:17?|?檔案?|?引用
EOF 叫 End of File Condition,自然不是字符
還有好多神馬:
tty: Ctrl+D
cmd: Ctrl+Z
std::basic_ios::eof()
std::istream_iterator(is)
...
2011年11月23日 17:19?|?檔案?|?引用
我記得Windows CMD中得按下^z后再按Enter才生效,而且是不區分在空行首與否的,即不像Unix的行首^D那樣直接flush buffer…
2012年1月18日 10:25?|?檔案?|?引用
注:若要參與討論可以到原作者blog參與,知識在交流中積累 http://www.ruanyifeng.com/blog/2011/11/eof.html轉載于:https://www.cnblogs.com/bizhu/archive/2012/06/03/2532602.html
總結
以上是生活随笔為你收集整理的EOF是什么?(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c#写数据到Excel慢
- 下一篇: 两个操作字符串的方法:读取指定位置的字符