【问题标题】:Do `fgetc`, `fputc`, `fgets` and `fputs` require a file opened in text mode, and `fread` and `fwrite` require a file in binary mode?`fgetc`、`fputc`、`fgets` 和 `fputs` 是否需要以文本模式打开的文件,而 `fread` 和 `fwrite` 需要以二进制模式打开的文件?
【发布时间】:2019-05-27 20:11:10
【问题描述】:

当对文件使用fgetcfputcfgetsfputs 时,我是否需要确保在文本模式而不是二进制模式下opened 文件?

当使用freadfwrite 时,我是否需要以二进制模式而不是文本模式opened 文件?

在 Linux 中,我想没有区别。但是如果在我目前无法访问的 Windows 中呢?

【问题讨论】:

  • 简答:“textmode”是微软创建的工件。
  • @wildplasser MACs(bitd)没有将行尾编码为'\r'吗? “文本模式”不是 MS 垄断的。即使在今天,使用“文本模式”,人们可能会看到一个字节顺序标记。
  • 警告,经过一些尝试,我编辑了我的答案,在 WIndows 下,文本模式也关注fputc/fgetc/../_read/_write,所以所有的 r/w 功能,而不仅仅是 fread fwrite,甚至微软的文档也忘记谈论 fgetc / fputc

标签: c io


【解决方案1】:

[在windows下使用mingw尝试后编辑]

文本模式和二进制模式的区别只存在于windows下,不仅涉及fwritefread,还涉及fgetc/fputc /.../_read/_write

在文本模式下,如果文件包含 \r\n 序列,则当您阅读时仅获取 \n,就像文件不包含 \r 时一样。在二进制模式下,两个字符被返回。注意 fread Microsoft Docs_read Microsoft Docs 谈到了这个案例,但不幸的是,fget fgetwc Microsoft Docs

中没有提到 fgetc

如果流以文本模式打开,则写入时,每个 \n 都被写入为 \r\n 对。请注意,替换对 fwrite 返回的大小没有影响。在二进制模式下,\n 写入不变,没有 \r 添加。注意 fwrite Microsoft Docs_write Microsoft Docs 谈到了那个案例,但不幸的是,fputc fputwc Microsoft Docs

中没有提到 fputc

幸运的是在 Windows 下的文本模式下读写函数是对称的,当你在文本模式下写入 foo\nbar 然后你读取生成的文件仍然在文本模式下你会得到 foo\nbar,但是有 foo\r\nbar 进入文件。

所以

在对文件使用 fgetc、fputc、fgets 和 fputs 时,是否需要确保以文本模式而不是二进制模式打开文件?

模式有影响

使用 fread 和 fwrite 时,是否需要以二进制模式而不是文本模式打开文件?

与其他功能一样,这是您的选择,但如果文件也被其他工具读/写,最好以文本模式打开,以防这些工具在读取时期望在 \n 之前出现 \r或在写作时产生对。当然你也可以用二进制模式打开,自己在\n前显式写一个\r。

如果该文件仅供您使用 C 进行读/写,则最好始终以二进制模式打开以在文件中仅包含您明确要求的内容。如果您使用 ftell/fseek,则会强制执行此操作。

正如ftell Microsoft Docs ftell 和 _ftelli64 中所说,可能无法反映在文本模式下打开的流的物理字节偏移量,因为文本模式会导致回车换行转换。

fseek 是最糟糕的,正如fseek Microsoft Docs 所说:

对于以文本模式打开的流,fseek 和 _fseeki64 的使用有限,因为回车换行转换会导致 fseek 和 _fseeki64 产生意外结果。唯一能保证对以文本模式打开的流起作用的 fseek 和 _fseeki64 操作是:

  • 相对于任何原点值的偏移量为 0。

  • 使用 fseek 时使用 ftell 调用或使用 _fseeki64 时调用 _ftelli64 返回偏移值,从文件开头查找。

【讨论】:

  • 所有输入输出函数都受 Windows 和类似旧系统下的文本模式与二进制模式设置的影响:如果流包含 \r\nfgetc() 将返回 '\n' fputc('\n') 确实会将 2 个字节写入输出流,'\n' 被转换为 \r\n
  • @chqrlie 你好,Microsoft Docs 中没有相关内容
  • 翻译发生在将流的缓冲区刷新到实际文件并从输入文件填充缓冲区时。使用哪个函数来读取或写入流并不重要。事实上,当使用O_TEXT 打开文件而不是使用O_BINARY 打开文件时,较低级别的 Posix 兼容 I/O 函数会执行此转换。
  • Microsoft 仍然记录从 CP/M 继承的古老 ^Z hack,其中文件大小 dd 没有字节精度,并且自 MS/DOS 2.0 以来已过时......这个平台太古怪了......
【解决方案2】:

没有。 所有 stdio 输入和输出函数是根据对fgetcfputc 的重复调用定义的。除了最上面的 POSIX 要求(主要锁定是针对整个更大的操作,而不是针对单个 fgetc/fputc 粒度),您自己在循环中调用 fputcfgetc 没有区别,或者做与fwritefread 等效。

如果您使用的 C 实现对文本文件的处理不同于二进制文件,则无论您使用哪个函数来执行操作,该处理都适用。

【讨论】:

  • 谢谢。 “所有 stdio 输入和输出函数都是根据对 fgetc 或 fputc 的重复调用定义的”。 fwritefread 是否“根据重复调用 fgetc 或 fputc”实现?
  • 这就是语言定义的方式。它们可以以任何实现者想要的方式实现,只要它们的行为就像是通过重复调用一样,所以当然大多数实现者会做一些事情来让它们更快。
猜你喜欢
  • 2019-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-27
  • 2015-10-07
  • 1970-01-01
  • 1970-01-01
  • 2014-05-27
相关资源
最近更新 更多