← All posts
August 17, 20243 min read

C++ ofstream Tips

What each std::ios flag actually does when opening a file for writing.

#c++#coding

ofstream opening flags

  • std::ios::ate — the whole file is available for writing, with the pointer starting at the end.
  • std::ios::app — writing always happens at the end; existing content can't be overwritten.
  • std::ios::trunc — deletes everything in the file and starts writing fresh.
  • std::ios::out — creates the file if it doesn't exist; writing behavior then depends on flags like trunc or ate.
  • std::ios::binary — the mode to reach for when generating binary files.

Common combinations

ofstream implies out on top of whatever mode you pass. The default open, with no flags at all, is effectively out | trunc: create the file if it's missing, wipe it if it exists, then write from byte zero.

#include <fstream>
 
std::ofstream f("x.txt");   // default: out | trunc
f << "hello\n";

out | app keeps existing content and appends every write at the end:

#include <fstream>
 
std::ofstream f("log.txt", std::ios::out | std::ios::app);
f << "new log line\n";

out | ate also starts at the end, but unlike app the pointer isn't locked — you can seekp(0) back to the start and overwrite bytes:

#include <fstream>
 
std::ofstream f("x.txt", std::ios::out | std::ios::ate);
f.seekp(0);
f << "overwrites from the start\n";

Binary output uses out | binary and write() on raw bytes:

#include <cstdint>
#include <fstream>
 
struct Header {
  std::int32_t magic;
  double       value;
};
 
Header h{0x12345678, 3.14};
std::ofstream f("data.bin", std::ios::out | std::ios::binary);
f.write(reinterpret_cast<const char*>(&h), sizeof(h));

Checking for failure

  • if (!f) — the stream converts to false when something is wrong; check it right after the open, since a bad path or missing permissions already fail there.
  • f.fail() — true if the last operation failed (the open included). Same condition as if (!f).
  • f.good() — true only while every error bit, EOF included, is clear, so it's a stricter test than fail().
  • Write, then check — the stream can go bad mid-write (disk full, device error):
f << data;
if (!f) { /* write failed */ }
  • f.flush() pushes buffered bytes out to the file; std::endl writes a newline and flushes. In a loop, emit '\n' and flush once at the end instead of paying for a flush on every line.
  • f.close() — flushes and releases the file. The destructor does the same automatically when the ofstream goes out of scope, so an explicit close() is only needed to reopen the stream or check the open state of another file first.