首页 > 解决方案 > 为什么 std::ofstream 会在没有 std::ios_base::trunc 的情况下截断?

问题描述

根据这个 C++ 参考:http : //www.cplusplus.com/reference/fstream/ofstream/ofstream/ ,默认的打开模式std::ofstream是 is并且它没有提到隐含的其他模式。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 错了吗?或者行为依赖于什么?

标签: c++ofstream

解决方案


根据[ofstream.cons]/itemdecl:2

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

因此,默认模式ofstreamout. 但是,根据[tab:filebuf.open.modes]out两者out | 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);

因此,默认模式fstreamin | 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)


推荐阅读