fstream
File stream classes for reading, writing, and random-access I/O on files; the <fstream> header provides ifstream, ofstream, and fstream.
std::ifstream / std::ofstream / std::fstreamsince C++98The <fstream> header provides RAII-managed file stream classes β std::ifstream for reading, std::ofstream for writing, and std::fstream for both β all deriving from the same std::basic_ios hierarchy that backs std::cin and std::cout.
Overview
std::ifstream, std::ofstream, and std::fstream are type aliases for std::basic_ifstream<char>, std::basic_ofstream<char>, and std::basic_fstream<char>. Wide-character variants (std::wifstream, std::wofstream, std::wfstream) cover wchar_t. Because they inherit from std::istream, std::ostream, and std::iostream respectively, all formatted I/O operators (>>, <<) and unformatted member functions (get, read, getline, write) behave identically to their cin/cout equivalents.
The file is opened in the constructor and closed by the destructor. open()/close() are available when you need to reuse a stream variable across multiple files.
Open modes
| Flag | Effect |
|---|---|
ios::in | Open for reading (default for ifstream) |
ios::out | Open for writing (default for ofstream) |
ios::binary | Disable newline translation; raw byte I/O |
ios::trunc | Discard existing file contents on open |
ios::app | All writes go to end of file; position is not seekable |
ios::ate | Seek to end immediately after open; position remains seekable |
std::fstream defaults to ios::in | ios::out but will not create the file if it does not exist β unlike std::ofstream, which implies ios::out | ios::trunc and creates the file. Flags combine with |:
std::ofstream log("app.log", std::ios::out | std::ios::app);Syntax
#include <fstream>
// Construct with immediate open (C++98)
std::ifstream in("data.txt");
std::ofstream out("result.txt");
std::fstream rw("store.bin", std::ios::in | std::ios::out | std::ios::binary);
// C++17: accept std::filesystem::path
#include <filesystem>
std::ifstream in2(std::filesystem::path{"data"} / "input.txt"); // C++17
// Deferred open
std::ifstream in3;
in3.open("late.txt");
if (!in3) { /* failbit set β file missing or permission denied */ }
// Exception-based error handling (exceptions are off by default)
in.exceptions(std::ios::failbit | std::ios::badbit);
// Streams are movable but not copyable (C++11)
std::ifstream make_reader(const char* path) {
return std::ifstream(path); // move on return (C++11)
}Examples
Formatted text I/O
#include <fstream>
#include <iostream>
#include <string>
int main() {
{
std::ofstream out("numbers.txt");
if (!out) return 1;
out << 42 << ' ' << 3.14 << '\n';
} // destructor flushes and closes
std::ifstream in("numbers.txt");
if (!in) return 1;
int n; double d;
in >> n >> d;
std::cout << "int=" << n << " double=" << d << '\n';
}Reading all lines from a file
The correct idiom is while (std::getline(...)), not while (!eof()):
#include <fstream>
#include <string>
#include <vector>
std::vector<std::string> read_lines(const char* path) {
std::ifstream in(path);
if (!in) throw std::runtime_error(std::string("cannot open: ") + path);
std::vector<std::string> lines;
std::string line;
while (std::getline(in, line))
lines.push_back(std::move(line)); // C++11
return lines;
}Binary I/O with read and write
#include <fstream>
#include <cstdint>
#include <vector>
#include <stdexcept>
struct Record { // must be trivially copyable
std::uint32_t id;
double value;
};
void write_records(const char* path, const std::vector<Record>& recs) {
std::ofstream out(path, std::ios::binary);
if (!out) throw std::runtime_error("write failed");
out.write(reinterpret_cast<const char*>(recs.data()),
static_cast<std::streamsize>(recs.size() * sizeof(Record)));
}
std::vector<Record> read_records(const char* path) {
std::ifstream in(path, std::ios::binary | std::ios::ate);
if (!in) throw std::runtime_error("read failed");
auto size = in.tellg(); // file size because ios::ate
in.seekg(0, std::ios::beg);
std::vector<Record> recs(static_cast<std::size_t>(size) / sizeof(Record));
in.read(reinterpret_cast<char*>(recs.data()), size);
if (!in) throw std::runtime_error("short read");
return recs;
}In-place editing with fstream
#include <fstream>
#include <cstdint>
void patch_field(const char* path, std::streamoff offset, std::uint32_t value) {
std::fstream fs(path, std::ios::in | std::ios::out | std::ios::binary);
if (!fs) throw std::runtime_error("cannot open for r/w");
fs.seekp(offset);
fs.write(reinterpret_cast<const char*>(&value), sizeof(value));
}Custom operator<< / operator>> for portable serialisation
Defining operators on std::ostream/std::istream (not on the file stream classes directly) means the same operators work with fstream, stringstream, and cout:
#include <fstream>
#include <string>
#include <stdexcept>
struct Config { std::string host; int port; bool tls; };
std::ostream& operator<<(std::ostream& os, const Config& c) {
return os << c.host << '\n' << c.port << '\n' << c.tls << '\n';
}
std::istream& operator>>(std::istream& is, Config& c) {
return is >> c.host >> c.port >> c.tls;
}
void save(const Config& c, const char* path) {
std::ofstream out(path);
if (!out) throw std::runtime_error("save failed");
out << c;
}
Config load(const char* path) {
std::ifstream in(path);
if (!in) throw std::runtime_error("load failed");
Config c;
if (!(in >> c)) throw std::runtime_error("parse error");
return c;
}Best Practices
Check stream state with if (!stream), not .good() or .is_open() separately.
The implicit bool conversion is equivalent to !fail() && !bad(). A failed open sets failbit, so if (!in) catches missing files. Calling .is_open() as an additional guard is redundant.
Use RAII scoping to guarantee flush-before-read ordering.
Wrap writes in a nested block so the destructor closes and flushes before a reader opens the same file. An explicit .close() call is the alternative when scoping is awkward; the destructor alone is always sufficient.
Prefer binary mode for structured data.
On Windows, text mode silently translates \n to \r\n on write and \r\n to \n on read. For binary formats or network-exchanged files, always pass std::ios::binary.
Use ios::ate + seekg(0) to query file size.
Opening with ate positions the get pointer at end-of-file; tellg() then returns the byte count without a separate stat(2) call.
Accept std::filesystem::path in C++17.
Constructors and open() overloads that accept std::filesystem::path were added in C++17. This is the correct type when composing paths programmatically β it handles directory separators and encoding portably across platforms.
Common Pitfalls
while (!stream.eof()) is always wrong.
eofbit is set after a read fails because end-of-file was reached, so the loop body executes one extra iteration with stale data. Use while (stream >> value) or while (std::getline(stream, line)) β both return a reference to the stream, which evaluates to false on any failure.
std::fstream does not create files that do not exist.
std::fstream fs("new.dat", ios::in | ios::out) fails if new.dat is absent. To create-then-open, add ios::trunc, or first create it via std::ofstream.
reinterpret_cast in binary I/O is only valid for trivially-copyable types.
read/write are correct only when the target type has no padding that matters, no virtual dispatch, and no pointer members. Structs that violate these properties require explicit field-by-field serialisation.
A seek is required when switching between reading and writing on std::fstream.
The standard requires a call to a positioning function (seekg, seekp, flush) between an output operation and a subsequent input operation on the same stream, and vice versa. Omitting it is undefined behaviour.
Streams are not copyable; move semantics require C++11.
Attempting to copy a stream fails to compile. Factory functions that return a stream must rely on move semantics (C++11) or NRVO.
See Also
<sstream>β in-memory string streams sharing the same interface as file streams<charconv>β locale-independent number-to-string conversion, useful when parsing individual fields read from a text file<filesystem>βstd::filesystem::pathfor constructing and introspecting file paths passed to stream constructors (C++17)