遇到一个打开文件方式“w+”和“a+”的问题 | 迟思堂工作室
A-A+

遇到一个打开文件方式“w+”和“a+”的问题

2015-01-17 13:20 代码生活 暂无评论 阅读 1,163 次

正在进行的项目中有个日志存储模块,需要在设备端将日志数据写到存储介质——其实就是硬盘,就是一个文件。在我测试时,发现上位机读取到的日志数据不全,明明登陆到设备看有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日 中午



如果本文对阁下有帮助,不妨赞助笔者以输出更多好文章,谢谢!
donate



标签:

给我留言