遇到一個開啟檔案方式“w+”和“a+”的問題
以前使用的是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日 中午