我在編寫學生成績管理系統時用到了寫文件和讀文件,發現讀文件總會顯示出亂碼,調試發現是因為用feof()做判斷,在讀文件最后時fread()多讀取了一次。我一開始的解決辦法是每次讀取完文件,都把最后一個節點(每一行數據都讀取放到鏈表的節點里)free掉。但這種方法明顯不是那么好。
feof(fp)用來測試fp所指向的文件當前狀態是否為“文件結束”。如果文件結束,則返回1,否則返回0。適合於二進制文件和文本文件。
但是,在實際使用feof時卻發生了問題:文件實際已經結束時feof還要再判斷一次才返回1
測試代碼如下:
1 #include <stdio.h> 2 int main() 3 { 4 char ch = 0; 5 int i = 0; 6 FILE* fp; 7 // 建立空文件 8 fp = fopen("Info.txt", "w"); 9 if(NULL == fp) 10 { 11 printf("Cannot open file!\n"); 12 return 0; 13 } 14 fclose(fp); 15 // 讀取空文件 16 fp = fopen("Info.txt", "r"); 17 if(NULL == fp) 18 { 19 printf("Cannot open file!\n"); 20 return 0; 21 } 22 while(!feof(fp)) 23 { 24 ch = fgetc(fp); 25 i++; 26 } 27 fclose(fp); 28 printf("fread times: %d\n" 29 "note: %c\n", i, ch); 30 return 0; 31 }
得到i為1,ch為空,說明feof()確實在文件為空狀態時判斷有誤。
上網搜索有人說先讀再判斷就不會有問題了,但試了一下還是不行。對此,C FAQ-12.3的解釋是“在C語言中,只有輸入例程試圖讀並失敗以后才能得到文件結束符。...fgets()在遇到文件結束符的時候返回NULL。實際上,在任何情況下,都完全沒必要使用feof()。”
把while(!feof(fp)){}換成while(!fgets(str, 0, fp)){}確實能保證判斷正確。但是,如果我在函數體里面還有其他讀操作如fread()的話則會出現影響。所以,最好的解決辦法是利用fseek()和ftell()配合來判斷文件指針位置:
1 fseek(fp, 0L, SEEK_END);//文件指針置於結尾 2 len1 = ftell(fp);//獲取結尾指針值 3 fseek(fp, 0L, SEEK_SET);//文件指針至於開頭 4 len2 = ftell(fp);//獲取開頭指針值 5 while(len2 != len1)//循環判斷 6 { 7 //do something 8 len2 = ftell(fp); 9 }
結果正確,無副作用。
其實還有另一種解決辦法,就是每次寫文件時在開頭寫入節點數,這樣下次讀文件時就知道要讀取多少數據了。
ChinaUnix上也有一篇帖子對於這個問題討論得很詳細,但是提供的方法不適合我當時的情況:http://bbs.chinaunix.net/thread-957347-1-1.html