feof()和EOF的用法—— C中文件結尾的判斷

昨天突然被一位朋友問到了關於文件結尾的程序問題。在用feof()判斷文件時,複製會多產生一個字符。


這個問題在大一的時候,老師上課就強調過,但那時只是模糊的記得個大概,記得這個函數如果用的不對就是會出現問題,解決是要先讀一下,然後再判斷,具體的什麼還真實忘了。而且平常經常用的EOF,所以這個問題自己並沒有一個特別好的概念。


現在寫的C語言多了,應該能理解這個問題存在的原因。所以就在網上找了好多關於這個問題的解釋,一看還真不少,但是好多都說的不清除,讓人看完後,感覺默默糊糊的。但是還是在其中學到了不少的知識。現在就把我的理解給寫出來,希望都能互相學習下。


查看 stdio.h 可以看到如下定義:

 

#define  EOF  (-1)

 

#define  _IOEOF  0x0010 
#define  feof(_stream)  ((_stream)->_flag & _IOEOF)

 

由此可以看出,這兩種方式的原理是不同的。

 

在這裏先說下EOF和feof()這個兩個宏定義,在我們學的課本中有這樣的描述。

EOF是不可輸出字符,因此不能在屏幕上顯示。由於字符的ASCII碼不可能出現-1,因此EOF定義爲-1是合適的。當讀入的字符值等於EOF時,表示讀入的已不是正常的字符而是文件結束符,但這適用對文本文件的讀寫。在二進制文件中,信息都是以數值方式存在的。EOF的值可能就是所要處理的二進制文件中的信息。這就出現了需要讀入有用數據卻被處理爲“文件結束“的情況。爲了解決這個問題,C提供了一個feof()函數,可以用它來判斷文件是否結束。feof(fp)用於測試fp所指向的文件的當前狀態是否爲“文件結束”。如果是,函數則返回的值是1(真),否則爲0(假)。

 

說了這兩個的定義,肯定還對二進制文件和文本文件的區別有些模糊(唉,因爲我當時就對這些搞不懂),那現在就回顧下這兩個文件的概念。C語言支持的是流式文件,它把文件看作由一個一個的字符(字節)數據組成的序列。根據數據的組織和操作形式,可以分爲ASCII文件和二進制文件。

ASCII文件又稱爲文本文件,它是在一個字節的存儲單元上存放一個字符(在外存中存放的是該字符的ASCII碼,每個字符將佔一個字節)。

二進制文件是把內存中的數據按其在內存中的存儲格式在磁盤上原樣保存。 

對字符而言,由於其外存存儲格式和內存表示格式相同,所以,在外存上也存放每個字符的ASCII碼。

 

 


但是說EOF只能用於文本文件,其實不然,這點不是特別的準確,還要看定義的變量的類型。

 

下面這段程序對文本文件和二進制文件都可以:


int c;
while((c=fgetc(fp)) != EOF)
{
printf("%X/n", c); 
}
如果讀到了FF,由於c定義爲int型,所以實際上c=0x000000FF,不等於EOF(-1=0xFFFFFFFF),因此不會誤判爲文件結尾。

 

但是如果把c定義爲char類型,就有可能產生混淆了。
char c;
while((c=fgetc(fp)) != EOF)
{
printf("%X/n", c); 
}
因爲文本文件中存儲的是ASCII碼,而ASCII碼中FF代表空值(blank),一般不使用,所以如果讀文件返回了FF,說明已經到了文本文件的結尾。但是如果是二進制文件,其中可能會包含FF,因此不能把讀到EOF作爲文件結束的條件,此時只能用feof()函數。

 

在VC裏,只有當文件位置指針(fp->_ptr)到了文件末尾,然後再發生讀/寫操作時,標誌位(fp->_flag)纔會被置爲含有_IOEOF。然後再調用feof(),纔會得到文件結束的信息。

因此,如果運行如下程序:
char c;
while(!feof(fp))
{
c = fgetc(fp);
printf("%X/n", c); 
}
會發現多輸出了一個FF,原因就是在讀完最後一個字符後,fp->flag仍然沒有被置爲_IOEOF,因而feof()仍然沒有探測到文件結尾。直到再次調用fgetc()執行讀操作,feof()才能探測到文件結尾。這樣就多輸出了一個-1(即FF)。


正確的寫法應該是:
char c;
c = fgetc(fp);
while(!feof(fp))
{
printf("%X/n", c); 
c = fgetc(fp);

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章