踩坑一:feof函數
函數原型:int feof( FILE *stream );
函數功能:如果讀取操作嘗試讀取超過文件末尾的位置,feof函數返回非0,否則返回0(函數 feof 只用於檢測流文件)
微軟官方文檔中關於feof描述:
部分翻譯:當超過文件末尾時,讀取操作返回文件結束指示符,直到流關閉或調用rewind,fsetpos,fseek或clearerr為止。例如,如果文件包含10個字節,你從文件中讀取了10個字節,feof會返回0,
因為雖然文件指針在文件的末尾,但是你沒有嘗試讀取超過文件的末尾,只有在你嘗試讀取第11個字節之后feof函數才會返回非零值
換個說法就是:當文件內部位置指針指向文件末尾時,並未立即置位 FILE 結構中的文件結束標記,只有再執行一次讀文件操作,才會置位結束標志,此后調用 feof 才會返回為真請讀者注意下這句話:“只有再執行一次讀文件操作,才會置位結束標志”
下面用斷點調試來測試feof的返回值:
測試代碼:
測試文本文件:
測試結果和運行結果:
從上面的測試結果可以看出,文本文件中只有三個數字,但循環卻執行了四次,在第三次循環時讀取了第三個數字后,文件內部的位置指針指向了文件末尾,但是此時用feof函數判斷的結果仍然為0,並不會跳出循環,
只有當第四次循環中再用fscanf_s讀取一次文件內容之后(此時fscanf_s的返回值是-1,也就是說讀取是失敗的,這也就是為什么第四次循環的ch的值沒有變化),再用feof判斷才會返回EOF(-1)
按照上面這種“先判斷、再讀取”寫法,如果一個文件含有n個字符,那么while循環的內部操作會運行n+1次,如果不想多循環一次那么需要在while循環內部增加判斷語句或者改成“先讀取、再判斷”
改法一:(while內部增加判斷)
改法二:(while內部增加判斷)
改法三:(先讀取再判斷)
踩坑二:ftell函數
函數原型:long ftell(FILE *fp);
函數功能:若函數調用成功,則返回文件位置指針當前位置相對於文件首的偏移字節數,否則返回-1L,(對於文本文件來說ftell的返回值是當前位置指針相對於文件起始位置的字節偏移量)
問題一:如果以追加讀寫的方式打開一個文本文件(文件內容為123),不進行任何I/O操作然后用ftell取得當前文件指針位置,請問ftell返回值為多少?
在微軟的官方文檔中有這樣一段話:
紅框中句子的意思:(當以追加的方式打開文件,在發生任何寫入操作前文件指針移動到文件的末尾),如果以追加的方式打開文件且沒有發生任何I/O操作,則文件指針在文件的開頭,鑒於本人英語水平有限沒怎么讀懂這句話
,感覺有點矛盾,下面就用斷點調試來解答問題一
文本文件:
打開方式:
調試結果:
從上面的調試結果可以看出,當以追加讀寫的方式打開文件時,在未發生任何I/O操作時ftell的返回值為0,意思是文件指針在文件開頭,這個在文件開頭的指針是下一次讀取的位置,而不是下一次寫入的位置(寫入的位置在文件的末尾),意思是如果是用fscanf_s讀取字符,那么讀取的字符就是第一個字符,如果是寫入字符那么就是在文件的最后一個字符的后一位寫入(就是文件的末尾),下面放一張圖方便理解
再回到上面那句話, (當以追加的方式打開文件,在發生任何寫入操作前文件指針移動到文件的末尾),如果以追加的方式打開文件且沒有發生任何I/O操作,則文件指針在文件的開頭,這句話的前半句說的在文件末尾的
文件指針指的是寫入的指針,而在文件開頭的指針是讀取的指針(並不是說讀取和寫入用的兩個不同的指針,是同一個指針,如果是兩個不同的指針的話那么無法確定ftell是返回的讀指針還是寫指針的位置)
意思是如果以追加讀寫的方式打開文件,如果發生讀操作那么讀取的是第一個字符,如果發生寫操作那么寫入的位置是文件的末尾(也就是最后一個字符的后一位)
補充:如果以上面為前提發生了一次寫入操作,此時ftell的返回值為多少?
測試文件:
測試代碼:
斷點調試結果:
從上面斷點調試的結果可以看出,ftell在發生I/O操作前返回值是0,也就是下一次讀取的位置,在發生一次寫入操作后,ftell的返回值是4,也就是一下次寫入的位置,如下圖