为什么在更新模式下读写之间总是需要使用fseek或fflush?

16

问题:我尝试使用fopen模式"r+"读取文件中的某个字符串,并将其修改后写回到原文件,但是没有起作用。

回答:在写入之前一定要调用fseek,这样可以回到您要覆盖的字符串的开头,并且因为在读/写“+”模式下读取和写入之间始终需要fseekfflush

我的问题是为什么在读/写“+”模式下读取和写入之间始终需要fseekfflush? Andrew Koenig的C陷阱与缺陷(1989)第5.2节提到这是由于向后兼容性问题。有人能详细解释吗?


1
“C陷阱与缺陷”已经有20多年的历史了 - 你真的需要向后兼容20年前的旧东西吗? - Dipstick
7
即使这本书已经有20年的历史,其中的规则仍然适用。 - Michael Burr
注意:此要求的相关 C 标准部分为 C11 7.21.5.3/7。 - M.M
3个回答

17

该库缓存输入和输出操作。请查看setvbuf()函数及其参数_IOFBF_IOLBF

fseek()fflush() 要求库提交缓冲操作。

标准规定在改变I/O方向之前需要进行搜索或清除(清除缓存)操作,以便让库更快完成操作。如果没有此限制,库必须针对每个I/O操作检查之前的操作是否为相同方向(读/写),并在I/O方向更改时自行触发刷新。 有了这个限制,库可以假设客户端在改变I/O方向之前已经执行了搜索/清除,并且可以省略方向检查。


5

因为这使得操作系统/库代码更简单。文件流可能有单独的读取写入缓冲区,需要额外的努力来确保它们始终同步。这会在不必要的时候降低性能。

因此,程序员需要在需要时明确地执行此操作。


5
阅读 Plauger 的 "The Standard C Library",了解为什么 (C89) 标准库的各种功能是如此设计的 - 特别是标准 I/O 库的部分。原因之一是 C 运行在非常多样化的系统和媒体上;像磁带这样的设备可能需要与您习惯思考的磁盘驱动器有所不同的处理方式。另外,在 Unix 上,考虑您的 'tty' 设备 - 它连接键盘、鼠标和屏幕 - 三个完全不同的硬件。协调它们之间的关系已经很棘手了;标准中的规定使这变得更加容易。
请注意,这是标准规定的。这来自 C11 标准,ISO/IEC 9899:2011,但在之前的版本中措辞相似: §7.21.5.3 函数 fopen 当使用 update 模式(模式参数值列表中的第二或第三个字符为“+”)打开文件时,可以在相关流上执行输入和输出。但是,在不经过 fflush 函数或文件定位函数 (fseekfsetposrewind) 的情况下,输出不能直接跟随输入,而且在没有经过文件定位函数的情况下,输入不能直接跟随输出,除非输入操作遇到文件结束。在某些实现中,用 update 模式打开(或创建)一个文本文件可能会打开(或创建)一个二进制流。

如果我想要写入一个已存在文件的前一半,然后立即读取剩余的旧数据,由于需要在读写模式之间刷新缓冲区,我是否需要使用fseek(pFile,0,SEEK_CUR)来确保缓冲区因为fseek()设置文件指针位置而被刷新?我曾经提出过这个问题,但没有得到答案!! - Thokchom
@JonathanLeffler 参考资料中进一步指出,如果“读取操作未达到文件结尾”,则在写入之前需要刷新。间接地说,这是否意味着如果读取到了文件结尾,并且我们想要从该结尾进行写入,我们不需要刷新缓冲区? - Thokchom
1
@Thokchom:四年后——抱歉耽搁了:标准规定如果你读取并遇到EOF,则可以立即进行写入而无需寻找操作。请参见我的回答中的引用。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接