Last updated: December 2025

C++ File I/O: ifstream, ofstream, Binary Files, std::filesystem & All Modes (2025)

By CoodeVerse Editorial Team ✓ 2025 Verified ⏱ 20 min read 🎯 Beginner–Intermediate 📦 C++11/17
Difficulty:
Beginner–Intermediate — Prerequisites: Classes & Objects, Exception Handling

⚡ Quick Answer: File I/O in C++

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

📂 Section 1

ifstream, ofstream, fstream — Which to Use

std::ifstream
Input (read) only. Default mode: ios::in.
Use: loading config, reading datasets, processing input files.
ifstream f("in.txt");
std::ofstream
Output (write) only. Default mode: ios::out|ios::trunc (overwrites).
Use: saving results, writing logs, generating output files.
ofstream f("out.txt");
std::fstream
Both read and write. Must specify mode explicitly.
Use: editing existing files, database-style record files.
fstream f("data.txt", ios::in|ios::out);
stream_basics.cpp — RAII open/close, stream stateC++
#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;
}
Output
> Line 1: Hello
> Line 2: World
> 42 3.14
Never call close() 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.
🎛️ Section 2

All Open Modes — Complete Reference Table

Mode flagMeaningTypical useCan combine with
ios::inOpen for reading. Fails if file doesn't exist.Default for ifstreamios::binary, ios::ate
ios::outOpen for writing. Creates file if absent. Truncates existing.Default for ofstreamios::app, ios::binary
ios::appAll writes go to end. File preserved. Cannot reposition writes.Log files, audit trailsios::out, ios::binary
ios::ateOpen and seek to end. Allows repositioning (unlike app).Random access from endios::in, ios::out
ios::truncTruncate to zero length on open. Implied by ios::out alone.Overwrite existing fileios::out
ios::binaryDisable newline translation (\\r\\n ↔ \\n on Windows).Any non-text dataAll modes
common mode combinationsC++
// 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);
📝 Section 3

Text File I/O — Read, Write, getline, CSV Parsing

text_io.cpp — word-by-word and formatted readingC++
#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;
}
Output
Alice: 95
Bob: 82
Carol: 91
Loaded 5 numbers
getline patterns — lines with spacesC++
#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;
}
Output
1: Hello World
2: C++ File I/O
3: Third line
[Alice] [95] [A]
csv_parser.cpp — parse CSV with istringstreamC++
#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;
}
Output
Alice: 95 (A)
Bob: 82 (B)
Carol: 71 (C)
read entire file into string — 3 methodsC++
#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;
}
Output (all 3 methods)
M1: Hello
World
M2: Hello
World
M3: Hello
World
💾 Section 4

Binary File I/O — Structs, Arrays, Vectors

binary_io.cpp — structs, arrays, and vector serializationC++
#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;
}
Output
1 Alice 3.85
2 Bob 3.72
Loaded 5 ints: 10 20 30 40 50
Binary portability warning. Binary files written on one machine may not read correctly on another if: (1) endianness differs (x86 = little-endian, some ARM = big-endian), (2) struct padding differs between compilers/platforms, (3) int or double size differs. For cross-platform files, use a text format (CSV, JSON) or a standardized binary format (Protocol Buffers, FlatBuffers). reinterpret_cast binary is fine for same-platform I/O.
🛡️ Section 5

Error Handling — is_open, Exceptions, seekg/tellg

file_errors.cpp — 3 error handling approaches + seekgC++
#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;
}
Never use 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.
🗂️ Section 6

std::filesystem (C++17) — Paths, Exists, Directory

filesystem_demo.cpp — C++17 file system operationsC++17
#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;
}
🏆 Section 7

DSA Examples: Graph Serialization, Competitive Programming I/O

1. Graph — save and load adjacency list

graph_io.cpp — read graph from file, count edgesC++
#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;
}
Output
Edges: 4
Saved successfully

2. Competitive programming — fast file I/O setup

competitive_io.cpp — freopen + sync_with_stdioC++
#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.
You need 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.
C++17 and later: #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.
C++ streams use RAII — the destructor automatically closes the file, even on exceptions. C requires manual 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.
Two safe patterns: (1) Check before reading: 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.

Related C++ Topics on CoodeVerse

Exception Handling Strings & String Processing STL Containers Deployment & Best Practices Debugging & Optimization Design Patterns Multithreading 📚 Full C++ Reading Materials

CoodeVerse Editorial Team

Senior C++ engineers and systems programmers. All code tested on Linux (GCC 13) and Windows (MSVC 2022).