KEEP K.I.S.S.

tk's blog

C文件读写一个盲点

《C陷阱与缺陷》第5.2节说道

为了保持与过去不能同时进行读写操作的程序的向下兼容性,一个输入操作不能随后直接紧跟一个输出操作,反之亦然。如果要同时进行输入和输出操作,必须在其中插入fseek函数的调用。

这里指以读+写模式打开的文件,既要进行读操作,还要进行写操作时需要注意的。

那么原因是什么?这个“向下兼容性”是指什么?

想看英文的直接戳这里 why fseek or fflush is always required between reading and writing in the read/write “+” modes

解释说:文件流的实现中可能会有单独的输入缓冲区和输出的缓冲区,分别用于fread 和 fwrite两类操作的缓冲优化。当一个fread操作紧跟一个fwrite操作时,逻辑上,fwrite操作之前必须要保证输入缓冲区中的内容已经被处理完毕(缓冲区的内容都提交给应用程序处理,即缓冲区为空),否则fwrite操作后,缓存与实际就不同步了(试想一下接下来调用fread,因为输入缓冲区残留有上次的内容,那么这次读取的就不是当前位置的文件内容了)。但是fread操作后紧跟另外一个fread操作就不需要这种保证(缓冲区的意义所在)。反之亦然。

这种保证,在实际文件流实现中,如果每次fread/fwrite都去检查并刷新缓冲区(fread检查输出缓冲区,fwrite检查输入缓冲区),会导致实现代码变的复杂,而且也会降低性能。并且只有在复合模式(可以同时读入和写出)下才需要这种检查。只读和只写则无需考虑。

所以标准库把这种只在某些情况下才需要做的工作留给了库的使用者。我觉得这挺符合C的principle的

那么,其他高级语言有这个问题吗?比如CPython,Ruby MRI等,这些依赖C实现的。

答案是有。

Python: Mixing read() and write() on Python files in Windows

Ruby: ruby read and write/change the same file

但是在CPython和Ruby MRI自带文档中都没有找到这样的提示,依赖C实现,也是没有太多注意吧。