Chapter 22 Advanced 50 min min read Updated 2026-04-10

File Handling (ifstream, ofstream, fstream)

Practice Questions →

In This Chapter

What Is It?

What Is File Handling in C++?

File handling allows C++ programs to read data from files and write data to files on disk. Instead of losing all data when a program terminates, you can persist student records, configuration settings, logs, and any structured data to files.

C++ provides file handling through the <fstream> header, which defines three stream classes:

  • ifstream (input file stream): Read from files
  • ofstream (output file stream): Write to files
  • fstream (file stream): Both read and write
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
    // Write to a file
    ofstream outFile("students.txt");
    outFile << "Arjun 85" << endl;
    outFile << "Priya 92" << endl;
    outFile << "Kiran 78" << endl;
    outFile.close();

    // Read from the file
    ifstream inFile("students.txt");
    string name;
    int marks;
    while (inFile >> name >> marks) {
        cout << name << " scored " << marks << endl;
    }
    inFile.close();
    return 0;
}

File streams inherit from the same iostream hierarchy as cin and cout. Everything Raghav knows about <<, >>, getline, and stream state applies directly to file streams.

Why Does It Matter?

Why Is File Handling Important?

1. Data Persistence

Without files, all program data is lost when the program exits. File handling lets Arjun save student records, configuration settings, and computation results that survive beyond the program's lifetime.

2. Practical Applications

Nearly every real-world application involves files: reading configuration files, parsing CSV data, logging errors, saving game state, processing large datasets that do not fit in memory, and communicating between programs through files.

3. Binary File I/O for Performance

Text files are human-readable but slow for large data. Binary files store data in the same format as memory, making reads and writes significantly faster. Database engines, image processors, and serialization libraries all use binary I/O.

4. Foundation for Advanced Topics

Understanding file streams prepares you for network streams, string streams (stringstream), and the C++17 <filesystem> library. The iostream abstraction is the same -- once you understand file I/O, you understand all C++ I/O.

5. Competitive Programming and Interviews

While competitive programming typically uses cin/cout, many online judges and companies use file-based I/O (freopen). Interview questions often involve parsing files, implementing CSV readers, or building config file parsers.

Detailed Explanation

Detailed Explanation

1. Opening and Closing Files

Files can be opened through the constructor or the open() method:

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    // Method 1: Open via constructor
    ofstream file1("output.txt");
    file1 << "Hello from constructor" << endl;
    file1.close();

    // Method 2: Open via open()
    ofstream file2;
    file2.open("output2.txt");
    if (file2.is_open()) {
        file2 << "Hello from open()" << endl;
        file2.close();
    } else {
        cerr << "Failed to open file" << endl;
    }

    // Destructor auto-closes, but explicit close is good practice
    {
        ofstream file3("output3.txt");
        file3 << "Auto-closed when file3 goes out of scope";
    } // file3.close() called automatically here
    return 0;
}

2. File Modes

File modes control how a file is opened:

ModeDescription
ios::inOpen for reading (default for ifstream)
ios::outOpen for writing (default for ofstream, truncates file)
ios::appAppend to end of file (does not truncate)
ios::binaryBinary mode (no text translation)
ios::truncTruncate file to zero length on open
ios::ateSeek to end after opening
#include <fstream>
using namespace std;

int main() {
    // Truncate and write (default for ofstream)
    ofstream f1("data.txt");
    f1 << "First write" << endl;
    f1.close();

    // Append mode: adds to existing content
    ofstream f2("data.txt", ios::app);
    f2 << "Appended line" << endl;
    f2.close();

    // Binary mode for raw data
    ofstream f3("data.bin", ios::binary);
    int num = 42;
    f3.write(reinterpret_cast<char*>(&num), sizeof(num));
    f3.close();

    // Combining modes with |
    fstream f4("data.txt", ios::in | ios::out | ios::app);
    f4.close();
    return 0;
}

3. Reading Line by Line (getline) and Word by Word (>>)

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
    // Create a test file
    ofstream out("poem.txt");
    out << "Twinkle twinkle little star" << endl;
    out << "How I wonder what you are" << endl;
    out.close();

    // Read line by line
    ifstream in1("poem.txt");
    string line;
    cout << "Line by line:" << endl;
    while (getline(in1, line)) {
        cout << "  " << line << endl;
    }
    in1.close();

    // Read word by word
    ifstream in2("poem.txt");
    string word;
    cout << "Word by word:" << endl;
    while (in2 >> word) {
        cout << "[" << word << "] ";
    }
    cout << endl;
    in2.close();
    return 0;
}

4. Checking File State

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    ifstream file("nonexistent.txt");

    // is_open(): was the file successfully opened?
    if (!file.is_open()) {
        cerr << "Could not open file" << endl;
    }

    // Stream state flags
    ifstream f("test.txt");
    // f.good()  -- no errors
    // f.eof()   -- end of file reached
    // f.fail()  -- logical error (e.g., reading int but found string)
    // f.bad()   -- serious I/O error (e.g., disk failure)

    // Common pattern: read until failure
    ifstream data("numbers.txt");
    int num;
    while (data >> num) {
        cout << num << " ";
    }
    if (data.eof()) cout << "\nReached end of file" << endl;
    if (data.fail()) cout << "Read failed (format error or EOF)" << endl;
    return 0;
}

5. Binary File I/O

#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;

struct Student {
    char name[50];
    int age;
    double gpa;
};

int main() {
    // Write binary
    Student s1;
    strcpy(s1.name, "Arjun");
    s1.age = 20;
    s1.gpa = 8.5;

    Student s2;
    strcpy(s2.name, "Priya");
    s2.age = 21;
    s2.gpa = 9.2;

    ofstream out("students.dat", ios::binary);
    out.write(reinterpret_cast<char*>(&s1), sizeof(Student));
    out.write(reinterpret_cast<char*>(&s2), sizeof(Student));
    out.close();

    // Read binary
    ifstream in("students.dat", ios::binary);
    Student temp;
    while (in.read(reinterpret_cast<char*>(&temp), sizeof(Student))) {
        cout << temp.name << ", Age: " << temp.age
             << ", GPA: " << temp.gpa << endl;
    }
    in.close();
    return 0;
}

6. File Position: seekg, seekp, tellg, tellp

#include <iostream>
#include <fstream>
using namespace std;

int main() {
    // seekg: set get (read) position
    // seekp: set put (write) position
    // tellg: get current read position
    // tellp: get current write position

    ofstream out("seektest.txt");
    out << "ABCDEFGHIJ";
    out.close();

    ifstream in("seektest.txt");

    // Read from position 3
    in.seekg(3);  // move to 4th byte (0-indexed)
    char ch;
    in.get(ch);
    cout << "At position 3: " << ch << endl;  // D

    // Tell current position
    cout << "Current position: " << in.tellg() << endl;  // 4

    // Seek from end
    in.seekg(-3, ios::end);
    in.get(ch);
    cout << "3 from end: " << ch << endl;  // H

    // Seek from current position
    in.seekg(1, ios::cur);
    in.get(ch);
    cout << "Skip 1 from current: " << ch << endl;  // J

    // Get file size
    in.seekg(0, ios::end);
    cout << "File size: " << in.tellg() << " bytes" << endl;
    in.close();
    return 0;
}

7. Practical Example: CSV Reader/Writer

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;

struct Student {
    string name;
    int age;
    double gpa;
};

void writeCSV(const string& filename, const vector<Student>& students) {
    ofstream file(filename);
    file << "Name,Age,GPA" << endl;  // header
    for (const auto& s : students) {
        file << s.name << "," << s.age << "," << s.gpa << endl;
    }
    file.close();
}

vector<Student> readCSV(const string& filename) {
    vector<Student> students;
    ifstream file(filename);
    string line;
    getline(file, line);  // skip header

    while (getline(file, line)) {
        stringstream ss(line);
        Student s;
        string token;
        getline(ss, s.name, ',');
        getline(ss, token, ',');
        s.age = stoi(token);
        getline(ss, token, ',');
        s.gpa = stod(token);
        students.push_back(s);
    }
    file.close();
    return students;
}

int main() {
    vector<Student> students = {
        {"Arjun", 20, 8.5}, {"Priya", 21, 9.2}, {"Kiran", 19, 7.8}
    };
    writeCSV("students.csv", students);

    auto loaded = readCSV("students.csv");
    for (const auto& s : loaded) {
        cout << s.name << ", Age: " << s.age << ", GPA: " << s.gpa << endl;
    }
    return 0;
}

8. Practical Example: Config File Parser

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <map>
using namespace std;

map<string, string> parseConfig(const string& filename) {
    map<string, string> config;
    ifstream file(filename);
    string line;

    while (getline(file, line)) {
        // Skip empty lines and comments
        if (line.empty() || line[0] == '#') continue;

        size_t pos = line.find('=');
        if (pos != string::npos) {
            string key = line.substr(0, pos);
            string value = line.substr(pos + 1);
            config[key] = value;
        }
    }
    file.close();
    return config;
}

int main() {
    // Create a config file
    ofstream out("app.conf");
    out << "# Application Config" << endl;
    out << "db_host=localhost" << endl;
    out << "db_port=3306" << endl;
    out << "app_name=StudentPortal" << endl;
    out << "debug=true" << endl;
    out.close();

    auto config = parseConfig("app.conf");
    for (auto& [key, value] : config) {
        cout << key << " = " << value << endl;
    }
    return 0;
}

Code Examples

No code examples available yet.

Common Mistakes

No common mistakes listed for this topic.

Summary

Summary coming soon.

Ready to Practice?

Test your understanding with 50+ practice questions on this topic.

Go to Practice Questions

Want to learn C++ with a live mentor?

Explore our C++ Masterclass