为什么 std::ofstream 会在没有 std::ios_base::trunc 的情况下截断?

Why does std::ofstream truncate without std::ios_base::trunc?

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

另一方面,Apache C++ 标准库用户指南 (http://stdcxx.apache.org/doc/stdlibug/30-3.html) 在第 30.3.1.2 段的注释中指出:"For output file streams the open mode out is equivalent to out|trunc, that is, you can omit the trunc flag. For bidirectional file streams, however, trunc must always be explicitly specified."

我试过这个代码:

#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错了吗?或者这种行为取决于什么?

根据 [ofstream.cons]/itemdecl:2:

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

因此,ofstream 的默认模式是 out。但是,根据 [tab:filebuf.open.modes], out and out | trunc both correspond to the stdio equivalent "w", so they are equivalent. Per C11 7.21.5.3:

w: truncate to zero length or create text file for writing

因此,说默认模式是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 corresponds to "r+", while in | out | trunc corresponds to "w+", so they are not equivalent. Per C11 7.21.5.3:

r+: open text file for update (reading and writing)
w+: truncate to zero length or create text file for update

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

(另请参阅:fopen on cppreference)