1. 程式人生 > >遇到一個開啟檔案方式“w+”和“a+”的問題

遇到一個開啟檔案方式“w+”和“a+”的問題

正在進行的專案中有個日誌儲存模組,需要在裝置端將日誌資料寫到儲存介質——其實就是硬碟,就是一個檔案。在我測試時,發現上位機讀取到的日誌資料不全,明明登陸到裝置看有100多KB,但讀到的日誌才2行,肯定有問題,另外,裝置儲存的日誌檔案內容也有亂碼出現。因為這個模組在其它專案一直使用,一直OK,現在出問題了,很鬱悶。而且該模組嵌入到其它大的功能模組,後來跟蹤、獨立測試,終於找到問題原因。當這個“小”問題解決時,都已經過了大半天了,架構如果複雜,以至於要細細跟蹤,不同平臺的差別,以至於無人注意。

以前使用的是ARM平臺,使用的儲存介質是eeprom;而現在是X86平臺,沒有了eeprom,所有的資料,儲存在硬碟上,而在linux上看,都是檔案。這點,在專案初期,沒什麼人留意因平臺差別而帶來的影響、工作量——不要怪我,因為那時我正被核心和根檔案系統搞得頭大,而且又沒參與架構程式碼的編寫。

問題出現有2個原因,一是沒注意eeprom和普通檔案的操作上的區別,二是開啟檔案的框架程式碼封裝得十分隱密(正因為封裝得好,使人認為操作所有“檔案”的程式碼都一樣)。

日誌模組抽象後的示例程式碼如下:

// 開啟檔案
fp = fopen(MYFILE, "a+");
// 讀頭部資料
fread(MyHead, 1, sizeof(MyHead), fp)
// 處理其它
...
// 寫入資料
fwrite(szText, 1, sizeof(szText), fp)
// 更新頭部資料
...
// 回到頭部
fseek(fp, 0, SEEK_SET)
// 更新頭部
fwrite(MyHead, 1, sizeof(MyHead), fp)

該模組使用檔案前面的資料作為頭部,保留了日誌長度等關鍵資訊。上位機讀取才得到2行,是因為該頭部中的日誌長度欄位除了第一次更新外,後面所有的寫操作都沒有更新——程式碼的確有更新“頭部”,但即不是正在的頭部,而是在日誌資料後面新增。簡單理解就是,最後的fseek並沒有回到檔案開頭處,而是在檔案結尾,於是fwrite就直接寫到檔案最後,這也解釋了日誌檔案為什麼會有亂碼,因為那是頭部資料。

查了fopen的引數,“a+”關鍵點如下:

“Repositioning operations (fseek, fsetpos, rewind) affects the next input operations, but output operations move the position back to the end of file. ”

這句話最後是說游標都回到檔案的末尾。因此,使用“a+”開啟檔案是不正確的。

網上有文章(http://blog.csdn.net/flyfy1/article/details/4763347)說得很好,就直接抄下來了:

r+ 和 w+ 的區別:

r+ 是可以直接寫在檔案上,讀取和寫入的游標都在檔案開頭。

w+ ,如果檔案已經存在,將建立一個新檔案覆蓋原檔案(很缺德啊……),並且支援讀取。

a+ 和 r+:

a+只能在檔案最後補充,游標在結尾。

r+可以覆蓋前面的內容,游標在開頭

最終的解決方法是,將原來程式碼的“a+”n改為“r+”,只改一個字元,即可解決問題。修改很簡單,但知道修改的原因卻要經過一番努力——而這,就是不為人知的背後的辛酸。

參考資料:

http://www.cplusplus.com/reference/cstdio/fopen/

http://blog.csdn.net/flyfy1/article/details/4763347

李遲,2015年1月17日 中午