C++ File I/O: ifstream, ofstream, Binary Files, std::filesystem & All Modes (2025)
⚡ Quick Answer: File I/O in C++
- ifstream — read files; ofstream — write files; fstream — read & write
- RAII — files close automatically when stream object goes out of scope
- Modes:
ios::in/ios::out/ios::app/ios::binary/ios::trunc - getline(file, line) — read whole lines including spaces
- Binary I/O:
ios::binary+read()/write()+reinterpret_cast - Error handling:
is_open()check orfile.exceptions(ios::failbit) - std::filesystem (C++17) — exists, copy, rename, list directory
File I/O is how your C++ programs persist data, load configuration, process datasets, and
communicate results to other tools. This guide covers everything: all three stream classes,
every open mode, text and binary read/write, error handling, the std::filesystem
library, file positioning, and four complete DSA examples.
Stream Classes
ifstream, ofstream, fstream
Open Modes
in/out/app/binary/trunc
Text I/O
getline, >>, CSV parsing
Binary I/O
read/write, structs, vectors
Error Handling
is_open, exceptions
std::filesystem
C++17 path operations
DSA Examples
Graph, CSV, serialization
FAQ
Common questions
ifstream, ofstream, fstream — Which to Use
ios::in.Use: loading config, reading datasets, processing input files.
ifstream f("in.txt");ios::out|ios::trunc (overwrites).Use: saving results, writing logs, generating output files.
ofstream f("out.txt");Use: editing existing files, database-style record files.
fstream f("data.txt", ios::in|ios::out);#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
// ── Write a file ──────────────────────────────────────────────
{
ofstream out("sample.txt"); // created/overwritten
if (!out.is_open())
throw runtime_error("Cannot write sample.txt");
out << "Line 1: Hello\n";
out << "Line 2: World\n";
out << 42 << " " << 3.14 << "\n";
} // ← ofstream destructor closes file (RAII)
// ── Read the file back ────────────────────────────────────────
ifstream in("sample.txt");
if (!in.is_open())
throw runtime_error("Cannot read sample.txt");
string line;
while (getline(in, line)) { // reads full lines
cout << "> " << line << endl;
}
// in closes automatically at end of scope
// ── Append to file ────────────────────────────────────────────
ofstream append("sample.txt", ios::app);
append << "Line 3: Appended\n";
// No explicit close() needed — RAII handles it
return 0;
}
> Line 1: Hello
> Line 2: World
> 42 3.14close() manually unless you need to reopen the file
or check for flush errors. C++ file streams close via their destructor (RAII) — even when an
exception is thrown. This makes file handling exception-safe by default.
All Open Modes — Complete Reference Table
| Mode flag | Meaning | Typical use | Can combine with |
|---|---|---|---|
| ios::in | Open for reading. Fails if file doesn't exist. | Default for ifstream | ios::binary, ios::ate |
| ios::out | Open for writing. Creates file if absent. Truncates existing. | Default for ofstream | ios::app, ios::binary |
| ios::app | All writes go to end. File preserved. Cannot reposition writes. | Log files, audit trails | ios::out, ios::binary |
| ios::ate | Open and seek to end. Allows repositioning (unlike app). | Random access from end | ios::in, ios::out |
| ios::trunc | Truncate to zero length on open. Implied by ios::out alone. | Overwrite existing file | ios::out |
| ios::binary | Disable newline translation (\\r\\n ↔ \\n on Windows). | Any non-text data | All modes |
// Read text file
ifstream f1("in.txt"); // ios::in (default)
// Write text file (create/overwrite)
ofstream f2("out.txt"); // ios::out|ios::trunc
// Append to text file
ofstream f3("log.txt", ios::app);
// Read binary file
ifstream f4("data.bin", ios::binary);
// Write binary file
ofstream f5("data.bin", ios::binary);
// Read + write binary (existing file)
fstream f6("db.bin", ios::in | ios::out | ios::binary);
// Create binary file (fails if exists)
ofstream f7("new.bin", ios::out | ios::binary | ios::excl);
Text File I/O — Read, Write, getline, CSV Parsing
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
// ── Write formatted data ───────────────────────────────────
{
ofstream out("scores.txt");
out << "Alice 95\n";
out << "Bob 82\n";
out << "Carol 91\n";
}
// ── Read name + score pairs ────────────────────────────────
ifstream in("scores.txt");
string name; int score;
while (in >> name >> score) { // >> skips whitespace
cout << name << ": " << score << endl;
}
// ── Write numbers ──────────────────────────────────────────
{
ofstream nums("numbers.txt");
vector<int> v = {5,3,8,1,9};
for (int x : v) nums << x << " ";
}
// ── Read numbers back ──────────────────────────────────────
ifstream nums("numbers.txt");
vector<int> loaded;
int x;
while (nums >> x) loaded.push_back(x); // reads until EOF
cout << "Loaded " << loaded.size() << " numbers\n";
return 0;
}
Alice: 95
Bob: 82
Carol: 91
Loaded 5 numbers#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
// Create file with multi-word lines
{
ofstream f("lines.txt");
f << "Hello World\n";
f << "C++ File I/O\n";
f << "Third line\n";
}
// Pattern 1: read all lines
ifstream f("lines.txt");
string line;
int lineNum = 0;
while (getline(f, line)) {
cout << ++lineNum << ": " << line << endl;
}
// Pattern 2: custom delimiter (semicolon-separated)
{
ofstream csv("semi.txt");
csv << "Alice;95;A";
}
ifstream csv("semi.txt");
string field;
while (getline(csv, field, ';')) { // split on ;
cout << "[" << field << "] ";
}
cout << endl;
return 0;
}
1: Hello World
2: C++ File I/O
3: Third line
[Alice] [95] [A] #include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int main() {
// Write CSV
{
ofstream csv("data.csv");
csv << "Name,Score,Grade\n";
csv << "Alice,95,A\n";
csv << "Bob,82,B\n";
csv << "Carol,71,C\n";
}
// Parse CSV — skip header, split each line on comma
ifstream csv("data.csv");
string line;
getline(csv, line); // skip header row
while (getline(csv, line)) {
istringstream row(line);
string name, grade;
int score;
getline(row, name, ',');
row >> score;
row.ignore(); // skip the comma
getline(row, grade, ',');
cout << name << ": " << score << " (" << grade << ")\n";
}
return 0;
}
Alice: 95 (A)
Bob: 82 (B)
Carol: 71 (C)#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
using namespace std;
int main() {
// Create test file
{ ofstream f("text.txt"); f << "Hello\nWorld\n"; }
// Method 1: ostringstream + rdbuf (simplest)
{
ifstream f("text.txt");
ostringstream ss;
ss << f.rdbuf();
string content = ss.str();
cout << "M1: " << content;
}
// Method 2: istreambuf_iterator (idiomatic)
{
ifstream f("text.txt");
string content(istreambuf_iterator<char>(f),
istreambuf_iterator<char>());
cout << "M2: " << content;
}
// Method 3: seekg for size, then read (fastest for large files)
{
ifstream f("text.txt");
f.seekg(0, ios::end);
auto size = f.tellg();
f.seekg(0, ios::beg);
string content(size, 0);
f.read(&content[0], size);
cout << "M3: " << content;
}
return 0;
}
M1: Hello
World
M2: Hello
World
M3: Hello
WorldBinary File I/O — Structs, Arrays, Vectors
#include <fstream>
#include <iostream>
#include <vector>
#include <cstring> // memset
using namespace std;
struct Student {
int id;
double gpa;
char name[32]; // fixed-width for binary I/O
};
int main() {
// ── Write struct to binary file ────────────────────────────
{
ofstream out("students.bin", ios::binary);
Student s1{1, 3.85, {}};
strncpy(s1.name, "Alice", sizeof(s1.name)-1);
Student s2{2, 3.72, {}};
strncpy(s2.name, "Bob", sizeof(s2.name)-1);
out.write(reinterpret_cast<const char*>(&s1), sizeof(Student));
out.write(reinterpret_cast<const char*>(&s2), sizeof(Student));
}
// ── Read structs back ──────────────────────────────────────
{
ifstream in("students.bin", ios::binary);
Student s;
while (in.read(reinterpret_cast<char*>(&s), sizeof(Student))) {
cout << s.id << " " << s.name << " " << s.gpa << endl;
}
}
// ── Vector binary serialization ────────────────────────────
{
vector<int> v = {10, 20, 30, 40, 50};
ofstream out("vec.bin", ios::binary);
size_t n = v.size();
out.write(reinterpret_cast<const char*>(&n), sizeof(n));
out.write(reinterpret_cast<const char*>(v.data()), n*sizeof(int));
}
{
ifstream in("vec.bin", ios::binary);
size_t n;
in.read(reinterpret_cast<char*>(&n), sizeof(n));
vector<int> v(n);
in.read(reinterpret_cast<char*>(v.data()), n*sizeof(int));
cout << "Loaded " << n << " ints: ";
for (int x : v) cout << x << " ";
cout << endl;
}
return 0;
}
1 Alice 3.85
2 Bob 3.72
Loaded 5 ints: 10 20 30 40 50 reinterpret_cast binary is fine for same-platform I/O.
Error Handling — is_open, Exceptions, seekg/tellg
#include <fstream>
#include <iostream>
#include <stdexcept>
using namespace std;
// Approach 1: Manual is_open check
void method1() {
ifstream f("missing.txt");
if (!f.is_open())
throw runtime_error("Cannot open missing.txt");
// ... use f
}
// Approach 2: Stream state checks during reading
void method2() {
ifstream f("data.txt");
int x;
while (f >> x) { /* process */ } // stops on EOF or error
if (f.bad()) throw runtime_error("I/O error during reading");
if (f.fail() && !f.eof()) throw runtime_error("Parse error");
}
// Approach 3: Enable exceptions (simplest for critical files)
void method3() {
ifstream f;
f.exceptions(ios::failbit | ios::badbit); // throw on any error
try {
f.open("config.txt"); // throws if file not found
// ... use f
} catch (const ios_base::failure& e) {
cout << "File error: " << e.what() << endl;
}
}
// seekg/tellg — file size and random access
long long fileSize(const string& path) {
ifstream f(path, ios::binary);
f.seekg(0, ios::end); // seek to end
auto size = f.tellg(); // position = size
f.seekg(0, ios::beg); // seek back to start
return (long long)size;
}
int main() {
try { method1(); }
catch (const exception& e) { cout << e.what() << endl; }
method3();
return 0;
}
while (!file.eof()) as a loop condition. It reads one
extra iteration — the last value gets processed twice because eof() only becomes
true after a read fails at end of file. Always use while (file >> x)
or while (getline(file, line)) — the stream's operator bool returns
false on both EOF and error.
std::filesystem (C++17) — Paths, Exists, Directory
#include <filesystem>
#include <iostream>
#include <fstream>
using namespace std;
namespace fs = filesystem;
int main() {
// ── Path operations ────────────────────────────────────────
fs::path p("data/input.txt");
cout << "Filename: " << p.filename() << endl; // input.txt
cout << "Extension: " << p.extension() << endl; // .txt
cout << "Parent: " << p.parent_path() << endl; // data
cout << "Absolute: " << fs::absolute(p) << endl;
// ── Create directories ─────────────────────────────────────
fs::create_directories("testdir/sub");
// ── Create test files ──────────────────────────────────────
{ ofstream f("testdir/a.txt"); f << "file a"; }
{ ofstream f("testdir/b.cpp"); f << "// b"; }
// ── Check existence ────────────────────────────────────────
cout << boolalpha;
cout << "exists: " << fs::exists("testdir") << endl;
cout << "is_dir: " << fs::is_directory("testdir") << endl;
cout << "is_file: " << fs::is_regular_file("testdir/a.txt") << endl;
cout << "file_size: " << fs::file_size("testdir/a.txt") << " bytes\n";
// ── List directory ─────────────────────────────────────────
cout << "testdir contents:\n";
for (const auto& entry : fs::directory_iterator("testdir")) {
cout << " " << entry.path().filename()
<< (entry.is_directory() ? "/" : "") << endl;
}
// ── Copy, rename, remove ───────────────────────────────────
fs::copy_file("testdir/a.txt", "testdir/a_copy.txt");
fs::rename("testdir/b.cpp", "testdir/renamed.cpp");
fs::remove_all("testdir"); // delete recursively
return 0;
}
DSA Examples: Graph Serialization, Competitive Programming I/O
1. Graph — save and load adjacency list
#include <fstream>
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;
class Graph {
int V;
vector<vector<int>> adj;
public:
explicit Graph(int v) : V(v), adj(v) {}
void load(const string& path) {
ifstream f(path);
if (!f.is_open()) throw runtime_error("Cannot open: " + path);
int u, v;
while (f >> u >> v) {
if (u < 0 || u >= V || v < 0 || v >= V)
throw out_of_range("Vertex out of range");
adj[u].push_back(v);
adj[v].push_back(u); // undirected
}
}
void save(const string& path) const {
ofstream f(path);
if (!f.is_open()) throw runtime_error("Cannot open: " + path);
for (int u=0; u<V; u++)
for (int v : adj[u])
if (u < v) f << u << " " << v << "\n"; // each edge once
}
int edgeCount() const noexcept {
int total = 0;
for (const auto& nb : adj) total += (int)nb.size();
return total / 2;
}
};
int main() {
try {
// Create graph file
{ ofstream f("graph.txt"); f << "0 1\n0 2\n1 2\n2 3\n"; }
Graph g(4);
g.load("graph.txt");
cout << "Edges: " << g.edgeCount() << endl;
g.save("graph_out.txt");
cout << "Saved successfully\n";
} catch (const exception& e) {
cerr << "Error: " << e.what() << endl;
}
return 0;
}
Edges: 4
Saved successfully2. Competitive programming — fast file I/O setup
#include <bits/stdc++.h>
using namespace std;
int main() {
// ── Fastest I/O setup for competitive programming ──────────
ios::sync_with_stdio(false); // decouple C and C++ I/O
cin.tie(nullptr); // don't flush cout before cin
// ── Redirect stdin/stdout to files ────────────────────────
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
// Then use cin/cout normally — they now read/write files
// ── Or use fstream directly ────────────────────────────────
// ifstream cin("input.txt"); // shadow cin for file input
// ofstream cout("output.txt"); // shadow cout for file output
// Example: read n numbers, output sum
int n; cin >> n;
long long sum = 0;
for (int i=0; i<n; i++) { int x; cin >> x; sum += x; }
cout << sum << "\n";
return 0;
}
Best Practices & Common Mistakes
✅ Always check is_open()
Or enable exceptions: f.exceptions(ios::failbit). Silent file-open failures are the #1 file I/O bug.
✅ Rely on RAII for closing
Let the destructor close the file. Manual close() is needed only if you want to reopen or check flush errors.
✅ Use getline for lines with spaces
file >> str stops at whitespace. getline(file, str) reads the entire line including spaces.
✅ Always use ios::binary for non-text
On Windows, text mode translates \r\n ↔ \n — this corrupts binary data like structs, images, and compressed files.
✅ Use std::filesystem for path ops
C++17's std::filesystem replaces platform-specific stat(), opendir(), FindFirstFile() with a single portable API.
✅ Buffer large writes
Write to an ostringstream first, then one file << ss.str() at the end. Multiple small writes are slower than one large write.
❌ while (!file.eof()) loop
Reads one extra iteration. Use while (file >> x) or while (getline(file, line)) — they test the stream state correctly.
❌ Relative paths without verification
The working directory is set by how the program is launched — not the source file location. Use absolute paths or verify with fs::exists().
FAQ / Interview Questions
ifstream is read-only — default mode ios::in. ofstream is write-only — default mode ios::out|ios::trunc (creates or overwrites). fstream is both read and write — you must specify the mode explicitly. All three use RAII (auto-close on destruction), support the same error-checking methods (is_open(), fail(), eof()), and the same seek methods (seekg/seekp, tellg/tellp).file >> str reads one whitespace-delimited token — it stops at the first space, tab, or newline. For a line like "John Doe", it only reads "John". getline(file, str) reads the entire line up to the newline character (consuming the newline but not storing it). Use >> for reading individual words or numbers; use getline for reading full lines. Mixing them requires cin.ignore() between them to consume the leftover newline.ios::binary any time you write non-text data: structs, integers, floats, images, compressed data. Without it (text mode), on Windows, the runtime translates \n to \r\n on write and \r\n to \n on read. For text files this is fine. For binary data, every \n byte (0x0A) gets an extra \r byte (0x0D) inserted — corrupting the file. On Linux, text and binary modes are identical — but always use ios::binary for binary data for portability.#include <filesystem>; namespace fs = std::filesystem; bool exists = fs::exists("file.txt");. To also confirm it's a regular file and not a directory: fs::is_regular_file("file.txt"). Pre-C++17: try to open with ifstream and check: ifstream f("file.txt"); bool exists = f.good();. The ifstream approach has a race condition (TOCTOU) in concurrent programs — if correctness matters, open the file and handle failure rather than checking first.fclose() — a thrown exception or early return skips it, leaking the file descriptor. C++ streams use << and >> with type safety; C uses format strings (%d, %f) with no compile-time type checking. C++ integrates with std::string, STL containers, and exception handling. C streams are faster for raw byte output but C++ streams are more correct and maintainable in modern code.ifstream f("file.txt"); if (!f.is_open()) { /* handle */ return; } /* use f */. (2) Enable exceptions: ifstream f; f.exceptions(ios::failbit); try { f.open("file.txt"); /* use f */ } catch (const ios_base::failure& e) { /* handle */ }. Never assume the file exists — file operations can fail due to permissions, disk errors, network timeouts (for network files), or the file being deleted between your check and open.Master C++ from file I/O to advanced topics
Structured lessons, 200+ exercises, completion certificate. Join 50,000+ students on CoodeVerse.