昨天突然被一位朋友問到了關于文件結尾的程序問題。在用feof()判斷文件時,復制會多產生一個字符。
這個問題在大一的時候,老師上課就強調過,但那時只是模糊的記得個大概,記得這個函數(shù)如果用的不對就是會出現(xiàn)問題,解決是要先讀一下,然后再判斷,具體的什么還真實忘了。而且平常經常用的EOF,所以這個問題自己并沒有一個特別好的概念。
現(xiàn)在寫的C語言多了,應該能理解這個問題存在的原因。所以就在網(wǎng)上找了好多關于這個問題的解釋,一看還真不少,但是好多都說的不清除,讓人看完后,感覺默默糊糊的。但是還是在其中學到了不少的知識?,F(xiàn)在就把我的理解給寫出來,希望都能互相學習下。
查看 stdio.h 可以看到如下定義:
#define EOF (-1)
#define _IOEOF 0x0010
#define feof(_stream) ((_stream)->_flag
& _IOEOF)
由此可以看出,這兩種方式的原理是不同的。
在這里先說下EOF和feof()這個兩個宏定義,在我們學的課本中有這樣的描述。
EOF是不可輸出字符,因此不能在屏幕上顯示。由于字符的ASCII碼不可能出現(xiàn)-1,因此EOF定義為-1是合適的。當讀入的字符值等于EOF時,表示讀入的已不是正常的字符而是文件結束符,但這適用對文本文件的讀寫。在二進制文件中,信息都是以數(shù)值方式存在的。EOF的值可能就是所要處理的二進制文件中的信息。這就出現(xiàn)了需要讀入有用數(shù)據(jù)卻被處理為“文件結束“的情況。為了解決這個問題,C提供了一個feof()函數(shù),可以用它來判斷文件是否結束。feof(fp)用于測試fp所指向的文件的當前狀態(tài)是否為“文件結束”。如果是,函數(shù)則返回的值是1(真),否則為0(假)。
說了這兩個的定義,肯定還對二進制文件和文本文件的區(qū)別有些模糊(唉,因為我當時就對這些搞不懂),那現(xiàn)在就回顧下這兩個文件的概念。C語言支持的是流式文件,它把文件看作由一個一個的字符(字節(jié))數(shù)據(jù)組成的序列。根據(jù)數(shù)據(jù)的組織和操作形式,可以分為ASCII文件和二進制文件。
ASCII文件又稱為文本文件,它是在一個字節(jié)的存儲單元上存放一個字符(在外存中存放的是該字符的ASCII碼,每個字符將占一個字節(jié))。
二進制文件是把內存中的數(shù)據(jù)按其在內存中的存儲格式在磁盤上原樣保存。
對字符而言,由于其外存存儲格式和內存表示格式相同,所以,在外存上也存放每個字符的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,說明已經到了文本文件的結尾。但是如果是二進制文件,其中可能會包含F(xiàn)F,因此不能把讀到EOF作為文件結束的條件,此時只能用feof()函數(shù)。
在VC里,只有當文件位置指針(fp->_ptr)到了文件末尾,然后再發(fā)生讀/寫操作時,標志位(fp->_flag)才會被置為含有_IOEOF。然后再調用feof(),才會得到文件結束的信息。
因此,如果運行如下程序:
char c;
while(!feof(fp))
{
c =
fgetc(fp);
printf("%X/n",
c);
}
會發(fā)現(xiàn)多輸出了一個FF,原因就是在讀完最后一個字符后,fp->flag仍然沒有被置為_IOEOF,因而feof()仍然沒有探測到文件結尾。直到再次調用fgetc()執(zhí)行讀操作,feof()才能探測到文件結尾。這樣就多輸出了一個-1(即FF)。
正確的寫法應該是:
char c;
c =
fgetc(fp);
while(!feof(fp))
{
printf("%X/n", c);
c =
fgetc(fp);
}