1. 程式人生 > >feof()和EOF的用法—— C中檔案結尾的判斷

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);
}