Skip to content
C++ Better Explained
Go back
C++ File Handling: Reading and Writing Files with fstream
Edit page

C++ File Handling: Reading and Writing Files with fstream

Almost every real C++ program needs to read from or write to files at some point — whether it is loading configuration, saving game state, processing data, or writing logs. C++ makes this straightforward through the <fstream> library.

This article covers everything you need to know: reading files line by line, writing and appending data, and handling errors.


The Three File Stream Classes

The <fstream> header gives you three classes:

ClassPurpose
ifstreamInput file stream — read from files
ofstreamOutput file stream — write to files
fstreamBoth reading and writing

For most tasks, use ifstream or ofstream. Only use fstream when you genuinely need to both read and write the same file.


Writing to a File with ofstream

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

int main() {
    ofstream outFile("scores.txt");

    if (!outFile.is_open()) {
        cout << "Error: could not open file" << endl;
        return 1;
    }

    outFile << "Alice: 95" << endl;
    outFile << "Bob: 87" << endl;
    outFile << "Charlie: 92" << endl;

    outFile.close();
    cout << "File written successfully" << endl;

    return 0;
}

This creates scores.txt (or overwrites it if it exists) and writes three lines. The << operator works exactly like cout.

Always call .close() when you are done — this flushes the buffer and releases the file handle. (Or use a block scope so the destructor closes it automatically.)


Reading a File with ifstream

Reading Line by Line

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

int main() {
    ifstream inFile("scores.txt");

    if (!inFile.is_open()) {
        cout << "Error: could not open file" << endl;
        return 1;
    }

    string line;
    while (getline(inFile, line)) {
        cout << line << endl;
    }

    inFile.close();
    return 0;
}

Output:

Alice: 95
Bob: 87
Charlie: 92

getline(inFile, line) reads one line at a time into the string variable line. The while loop continues until the end of the file.

Reading Word by Word

ifstream inFile("scores.txt");
string word;

while (inFile >> word) {
    cout << word << " ";
}

The >> operator reads whitespace-delimited tokens, so it is useful for reading individual words or numbers.


Appending to a File

By default, ofstream overwrites the file. To add to the end without destroying existing content, use ios::app:

#include <fstream>
using namespace std;

int main() {
    ofstream outFile("scores.txt", ios::app);

    if (outFile.is_open()) {
        outFile << "Diana: 98" << endl;
        outFile.close();
    }

    return 0;
}

Now scores.txt will have four lines — the original three plus Diana’s score.


File Open Modes

You can combine modes using the | operator:

ModeMeaning
ios::inOpen for reading
ios::outOpen for writing (default for ofstream)
ios::appAppend to end of file
ios::truncTruncate (clear) file on open (default for ofstream)
ios::binaryOpen in binary mode
// Open for both reading and writing, without truncating
fstream file("data.txt", ios::in | ios::out);

Reading Numbers from a File

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

int main() {
    // First write some numbers
    ofstream outFile("numbers.txt");
    outFile << "10 20 30 40 50" << endl;
    outFile.close();

    // Now read them back
    ifstream inFile("numbers.txt");
    int num;
    int total = 0;

    while (inFile >> num) {
        total += num;
    }

    inFile.close();
    cout << "Total: " << total << endl; // Output: Total: 150

    return 0;
}

Practical Example: Simple Student Grade Logger

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

void saveGrade(const string& name, int grade) {
    ofstream file("grades.txt", ios::app);
    if (file.is_open()) {
        file << name << " " << grade << endl;
        file.close();
    }
}

void showAllGrades() {
    ifstream file("grades.txt");
    if (!file.is_open()) {
        cout << "No grades recorded yet." << endl;
        return;
    }

    string name;
    int grade;
    cout << "--- Grade Record ---" << endl;
    while (file >> name >> grade) {
        cout << name << ": " << grade << endl;
    }
    file.close();
}

int main() {
    saveGrade("Alice", 92);
    saveGrade("Bob", 85);
    saveGrade("Charlie", 78);

    showAllGrades();
    return 0;
}

Output:

--- Grade Record ---
Alice: 92
Bob: 85
Charlie: 78

Error Handling Best Practices

Always check that a file opened successfully before using it:

ifstream file("data.txt");

if (!file) {  // Equivalent to !file.is_open()
    cerr << "Error: could not open data.txt" << endl;
    return 1;
}

Use cerr for error messages — it goes to the error stream rather than standard output.

You can also check for specific error states:

if (file.fail()) { /* read/write error */ }
if (file.eof())  { /* end of file reached */ }
if (file.bad())  { /* unrecoverable error */ }

Using RAII — Let the Destructor Close the File

You do not have to call .close() manually if you scope the file stream correctly — the destructor closes it automatically when it goes out of scope:

{
    ofstream file("log.txt");
    file << "Log entry" << endl;
} // file.close() called automatically here

This is the preferred modern C++ approach — it guarantees the file is closed even if an exception is thrown.



Edit page
Share this post on:

Previous Post
Bubble Sort in C++: How It Works with Full Source Code
Next Post
C++ Pass by Value, Reference & Pointer: Complete Guide