在windows作業系統中,大家在編寫帶有檔案操作的程式時,有時候會遇到一種奇怪的現象,在對一個檔案以文本方式進行讀取的過程中,讀到中途還沒到檔案末尾時feof()函數就意外地為真,這讓人很驚訝,一時難以找到原因,實際上,這是ASCII碼0x1A在作怪。運行下面程式:
int main(void)
{
int i;
unsigned char c;
FILE *fp;
fp=fopen("test.dat", "w");
fprintf(fp, "abc/x1A def");
fclose(fp);
fp=fopen("test.dat", "r");
for(i=0; i<=7; ++i)
{
fread(&c, sizeof(char), 1, fp);
printf("%02X feof=%d/n", c, feof(fp));
}
fclose(fp);
return 0;
}
啟動並執行結果是:
61 feof=0
62 feof=0
63 feof=0
63 feof=16
63 feof=16
63 feof=16
63 feof=16
63 feof=16
從以上結果可見,在讀到第四個字元就是0x1A的時候,feof為真了。這種現象目前只發現存在於windows中,unix/linux沒有。為什麼會這樣呢?
0x1A在ASCII碼中代表EOF,在過去,ASCII碼EOF曾經在unix/linux中被作為檔案結束符使用,微軟繼承了這個傳統,也以EOF作為檔案的結束符,不過,筆者手裡的一些資料表明,微軟在dos5.0以後就拋棄了這種做法。但實際情況是,筆者在dos6.22、windows3.1、windows3.2、windows9x、windows2k、xp、2003都存在這種問題。同時,這種問題是系統還是庫函數造成的也有待進一步查證,由於沒有源碼,無法證實,如果哪位朋友有這方面的資料,希望可以共用。另一方面,鑒於dos/windows下所有主流編譯器例如VC、BCB、gcc、tc2.0、bc3.1等都是同樣的結果,筆者傾向於這是系統原因造成的。
那麼這種0x1A現象是否符合標準呢?C89/C99是這樣定義檔案流的兩種方式的:
7.19.2 Streams
A text stream is an ordered sequence of characters composed into lines, each line consisting of zero or more characters plus a terminating new-line character. Whether the last line requires a terminating new-line character is implementation-defined. Characters may have to be added, altered, or deleted on input and output to conform to differing conventions for representing text in the host environment. Thus, there need not be a one-to-one correspondence between the characters in a stream and those in the external representation.
A binary stream is an ordered sequence of characters that can transparently record internal data. Data read in from a binary stream shall compare equal to the data that were earlier written out to that stream, under the same implementation.
規定中說明,文本方式可以對輸入輸出檔案的字元進行添加、替換和刪除,0x1A現象是否屬於這三種情況之列呢?筆者認為不屬於,因為它在邏輯上使檔案發生了截斷,已經不僅僅是對字元的添加、替換或刪除了。如果這被證實是非法的話,那麼,無論是庫函數還是系統的原因,庫函數都有責任對此進行修正。
如何解決這個問題?其實標準已經給出了其中一種解決方案,由於二進位方式的輸入輸出是一一對應的,字元不會產生變化,那麼用二進位讀取就不會產生這個問題,事實也證明是可以的,只是存在一點麻煩,由於windows下會把/n轉換為/r/n,二進位讀取時必須對此轉換進行堙別和還原,這會增加代碼的複雜性,這種麻煩很讓人討厭。因此筆者嘗試在低級函數中尋找更好的答案,但讓人失望的是幾款主流編譯器的低級函數即使在文本方式下都沒有對/r/n進行轉換,由於低級函數的行為跟標準無關,如果有哪款編譯器的低級函數對/r/n進行了還原,那就是比二進位方式更好的解決方案。
但無論如何,以上方法都不完美,這是否意味著windows的文本方式沒有任何意義?這實在讓人沮喪。如果哪位朋友對此有深入的研究,歡迎一起討論。