I am trying to open a file for output and append to it. After appending to it, I want to move my output position somewhere else in the file and overwrite existing data. As I understand it, std::ios_base::app
will force all writes to be at the end of the file which is not what I want to do. As such, I believe std::ios_base::ate
is the correct flag to pass to std::ofstream::open()
. However, it seems to be not working as expected:
// g++ test.cpp
// clang++ test.cpp
// with and without -std=c++11
#include <iostream>
#include <fstream>
int main() {
std::streampos fin, at;
{
std::ofstream initial;
initial.open("test", std::ios_base::out | std::ios_base::binary);
if ( not initial.good() ) {
std::cerr << "initial bad open" << std::endl;
return 1;
}
int b = 100;
initial.write((char*)&b, sizeof(b));
initial.flush();
if ( not initial.good() ) {
std::cerr << "initial write bad" << std::endl;
return 1;
}
fin = initial.tellp();
}
{
std::ofstream check;
check.open("test", std::ios_base::out | std::ios_base::binary | std::ios_base::ate);
if ( not check.good() ) {
std::cerr << "check bad open" << std::endl;
return 1;
}
at = check.tellp();
if ( fin != at ) {
std::cerr << "opened at wrong position!\nfin:\t" << fin << "\n" << "at:\t" << at << std::endl;
return 1;
}
int bb = 200;
check.write((char*)&bb, sizeof(bb));
check.flush();
if ( not check.good() ) {
std::cerr << "check write bad" << std::endl;
return 1;
}
at = check.tellp();
}
if ( (fin + std::streampos(sizeof(int))) != at ) {
std::cerr << "overwrite?\nfin:\t" << fin << "\n" << "at:\t" << at << std::endl;
return 1;
}
return 0;
}
In particular, it seems that std::ios_base::ate
does not move the initial output pointer to the end with the example seen above. Obviously this would result in the first write overwriting the beginning of the file (which is what caused my trouble).
It seems that either the implementation is incorrect or else cplusplus.com is incorrect ("The output position starts at the end of the file.") and cppreference.com is ambiguous ("seek to the end of stream immediately after open": which stream?).
There is obviously an easy workaround: just use stream.seekp(0, std::ios_base::end)
.
So my question is thus: is my code incorrect? Is the implementation incorrect? Are the reference sites incorrect? Any insight would be appreciated.
As you can see from the following chart in N4296 [filebuf.members]
The combination binary | out
will open the file in the stdio
equivalent of "wb"
, which will truncate to zero length or create binary file for writing
(N1570 7.21.5.2).
As counterintuitive as it sounds for an ofstream
, you will need to add the in
flag if you don't want your file to be truncated, or app
if you want to avoid truncation and seek to the end of the file on each write.
Bonus tip: Unlike fstream
, ifstream
and ofstream
will automatically or std::ios_base::in
and std::ios_base::out
respectively with any flags you provide to the constructor or to open
. You can also use the object itself to access the flags:
std::ofstream check("test", check.in | check.binary | check.ate);
The checks for good
can also be shortened to if (!initial)
etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With