Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does std::ofstream truncate without std::ios_base::trunc? [duplicate]

Tags:

c++

ofstream

According to this C++ reference: http://www.cplusplus.com/reference/fstream/ofstream/ofstream/, the default open mode for std::ofstream is ios_base::out and it mentions no implict other modes. Therefore I would expect that if I overwrite a large file with a small file, the "exceeding" part of the large file should stay untouched and only the first part of the file should be replaced by the new, shorter data.

On the other hand, the Apache C++ Standard Library User's Guide (http://stdcxx.apache.org/doc/stdlibug/30-3.html) states in the note to paragraph 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."

I have tried this code:

#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();
}

Both, with g++ 8.1 on Windows and g++ 6.3 on Linux, the Apache documentation seems to be right. The large file is truncated, nothing remains after writing the shorter string with the second file stream.

Why is it like that? Is cplusplus.com wrong? Or on what does the behaviour depent?

like image 996
Benjamin Bihler Avatar asked Jul 17 '19 06:07

Benjamin Bihler


1 Answers

Per [ofstream.cons]/itemdecl:2:

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

Therefore, the default mode for ofstream is out. However, per [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

Thus, it is correct to say that the default mode is out, and it is also correct to say that the default mode is equivalent to out | trunc. This is guaranteed behavior.

On the other hand, per [fstream.cons]/itemdecl:2:

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

Therefore, the default mode for fstream is in | out. Per [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

Therefore, fstream does not truncate unless you specify trunc. Note that if the desired file does not exist, r+ will fail instead of creating the file. In contrast, w and w+ both create a new file in this case.

(See also: fopen on cppreference)

like image 53
L. F. Avatar answered Nov 12 '22 02:11

L. F.