【问题标题】:Why does std::ofstream truncate without std::ios_base::trunc? [duplicate]为什么 std::ofstream 会在没有 std::ios_base::trunc 的情况下截断? [复制]
【发布时间】:2019-07-17 06:41:50
【问题描述】:

根据这个 C++ 参考:http://www.cplusplus.com/reference/fstream/ofstream/ofstream/std::ofstream 的默认打开模式是ios_base::out,它没有提及其他隐含模式。因此,我希望如果我用一个小文件覆盖一个大文件,大文件的“超出”部分应该保持不变,只有文件的第一部分应该被新的、更短的数据替换。

另一方面,Apache C++ 标准库用户指南 (http://stdcxx.apache.org/doc/stdlibug/30-3.html) 在第 30.3.1.2 段的注释中指出:“对于输出文件流,打开模式 out 等价于 out|trunc,也就是说,您可以省略 trunc 标志。但是,对于双向文件流,必须始终明确指定 trunc。"

我试过这段代码:

#include <fstream>

int main()
{
    std::ofstream aFileStream("a.out", std::ios_base::out);
    aFileStream << "Hello world!";
    aFileStream.close();

    std::ofstream aFileStream2("a.out", std::ios::out);
    aFileStream2 << "Bye!";
    aFileStream2.close();
}

对于 Windows 上的 g++ 8.1 和 Linux 上的 g++ 6.3,Apache 文档似乎都是正确的。大文件被截断,用第二个文件流写入较短的字符串后什么都没有了。

为什么会这样? cplusplus.com 错了吗?或者行为取决于什么?

【问题讨论】:

  • 根据cppreference,out 将删除现有文件中的内容(因此覆盖所有内容)

标签: c++ ofstream


【解决方案1】:

[ofstream.cons]/itemdecl:2:

explicit basic_ofstream(const char* s,
                        ios_base::openmode mode = ios_base::out);

因此,ofstream 的默认模式为out。但是,根据[tab:filebuf.open.modes]outout | trunc 都对应于 stdio 等价物"w",因此它们是等价的。每C11 7.21.5.3

w: 截断到零长度或创建文本文件进行写入

因此,说默认模式是out是正确的,说默认模式等价于out | trunc也是正确的。这是有保证的行为。

另一方面,根据[fstream.cons]/itemdecl:2

explicit basic_fstream(
  const char* s,
  ios_base::openmode mode = ios_base::in | ios_base::out);

因此,fstream 的默认模式为in | out。根据[tab:filebuf.open.modes]in | out对应"r+",而in | out | trunc对应"w+",所以它们不等价。每C11 7.21.5.3

r+:打开文本文件进行更新(读写)
w+:截断为零长度或创建文本文件进行更新

因此,fstream 不会截断,除非您指定 trunc。 请注意,如果所需文件不存在,r+ 将失败而不是创建文件。相比之下,ww+ 在这种情况下都会创建一个新文件。

(另见:fopen 在 cppreference 上)

【讨论】:

  • 很好的答案——你能否也链接引用的 C 标准?这将使答案变得更好。
  • @FelixDombek 不幸的是,我不知道官方的 HTML C 标准草案版本。 C11 最终草案的 PDF 版本可在open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 获得。等一下……
  • @FelixDombek 添加了标准和 cppreference 的链接。
  • 谢谢,太棒了 - 等等,你的哈希锚名称是如何工作的?!它看起来一点也不像adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/…中给出的标准
  • @FelixDombek Magic :) 我使用的是 Firefox,我在左侧的导航面板上单击鼠标右键,然后选择“复制链接位置”,然后就可以了。
猜你喜欢
  • 2012-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-23
  • 2020-11-05
相关资源
最近更新 更多