File Streams

C++ · Lesson 28 of 32 6 min read Updated

Everything a program stores in variables disappears when it ends. To keep data between runs, such as marks, sales or settings, you save it in a file. C++ reads and writes files with file streams, which work exactly like std::cin and std::cout, so most of what you already know carries over.

In this lesson you will write and read text files, process data line by line and word by word, parse a CSV file, append to a file, and manage files and folders with std::filesystem.

Writing a file with std::ofstream

The <fstream> header provides three stream classes: std::ofstream (output file stream) for writing, std::ifstream (input file stream) for reading, and std::fstream for both. Creating an ofstream with a file name opens the file, creating it if needed and replacing any existing content.

C++
#include <fstream>
#include <print>

int main() {
    std::ofstream out("marks.txt");
    if (!out) {
        std::println("Could not create marks.txt");
        return 1;
    }

    out << "Aarav 82\n";
    out << "Priya 95\n";
    std::println(out, "Kabir {}", 77);      // C++23: println into any stream

    std::println("Saved 3 records");
}                                           // out is closed automatically here
Output
Saved 3 records

After the program runs, marks.txt contains:

Text
Aarav 82
Priya 95
Kabir 77

You write to a file stream with <<, just like std::cout. C++23 also lets you pass a stream as the first argument of std::print and std::println. Always check that the file opened: !out is true if opening failed, for example because the folder does not exist or you have no permission. You do not need to call close(): the stream is an RAII object and closes the file in its destructor.

Reading a file line by line

To read, create an ifstream and use std::getline in a loop. The loop stops automatically at the end of the file, because getline then returns a stream in a failed state, which counts as false.

C++
#include <fstream>
#include <print>
#include <string>

int main() {
    std::ifstream in("marks.txt");
    if (!in) {
        std::println("marks.txt not found");
        return 1;
    }

    std::string line;
    int count = 0;
    while (std::getline(in, line)) {
        ++count;
        std::println("{}: {}", count, line);
    }
}
Output
1: Aarav 82
2: Priya 95
3: Kabir 77

Reading words and numbers

When a file contains values separated by spaces, read them with >> exactly as you would from the keyboard. The condition in >> name >> marks is true only if both values were read successfully.

C++
#include <fstream>
#include <print>
#include <string>

int main() {
    std::ifstream in("marks.txt");
    std::string name;
    int marks = 0;

    int total = 0;
    int students = 0;
    std::string topper;
    int best = -1;

    while (in >> name >> marks) {
        total += marks;
        ++students;
        if (marks > best) {
            best = marks;
            topper = name;
        }
    }

    std::println("Students: {}", students);
    std::println("Average: {:.2f}", static_cast<double>(total) / students);
    std::println("Topper: {} with {}", topper, best);
}
Output
Students: 3
Average: 84.67
Topper: Priya with 95

Appending and parsing CSV

Open an ofstream with std::ios::app (append) to add to the end of a file instead of replacing it. CSV (comma-separated values) files are a common way to exchange data with spreadsheets. To split a line at commas, put the line into a std::istringstream (from <sstream>), a stream that reads from a string, and call std::getline with ',' as the separator.

C++
#include <fstream>
#include <print>
#include <sstream>
#include <string>

int main() {
    {
        std::ofstream out("sales.csv");                   // start fresh
        out << "date,item,amount\n";
        out << "2026-10-01,Notebook,120\n";
        out << "2026-10-01,Pen,30\n";
    }
    {
        std::ofstream more("sales.csv", std::ios::app);   // add at the end
        more << "2026-10-02,Geometry box,250\n";
    }

    std::ifstream in("sales.csv");
    std::string line;
    std::getline(in, line);                               // skip the header row

    int total = 0;
    while (std::getline(in, line)) {
        std::istringstream fields(line);
        std::string date, item, amount;
        std::getline(fields, date, ',');
        std::getline(fields, item, ',');
        std::getline(fields, amount, ',');

        total += std::stoi(amount);
        std::println("{:<14}{:>5}", item, amount);
    }
    std::println("{:<14}{:>5}", "Total", total);
}
Output
Notebook        120
Pen              30
Geometry box    250
Total           400

The extra braces around each writing block make the stream close (and flush its data to disk) before the file is opened again for reading. Real CSV files can contain quoted fields with commas inside them; for those, use a tested CSV library rather than this simple split.

Open modeMeaning
(default for ofstream)Create the file or replace its content
std::ios::appWrite at the end, keeping existing content
std::ios::binaryNo newline translation; for images and other non-text data
std::ios::in with std::fstreamOpen for reading as well

On Windows, text-mode streams turn \n into the two characters \r\n when writing and back when reading. Binary mode turns that translation off.

Files and folders with std::filesystem

C++17 added std::filesystem (header <filesystem>) for working with paths, folders and file information, the same way on every operating system.

C++
#include <filesystem>
#include <fstream>
#include <print>

namespace fs = std::filesystem;

int main() {
    fs::create_directories("reports/2026");        // creates both folders if needed
    fs::path report = "reports/2026/october.txt";

    {
        std::ofstream out(report);
        out << "Monthly sales report\n";
    }

    std::println("Exists? {}", fs::exists(report));
    std::println("Name: {}, extension: {}", report.filename().string(), report.extension().string());

    fs::remove(report);
    std::println("Exists after remove? {}", fs::exists(report));
}
Output
Exists? true
Name: october.txt, extension: .txt
Exists after remove? false

A fs::path understands folder separators on every system, and functions such as exists, file_size, remove, rename and directory_iterator cover most file-management tasks.

Common mistakes

  • Not checking that the file opened. Reading from a stream that failed to open silently reads nothing.
  • Relative paths. "marks.txt" is looked up in the program's current working directory, which in an IDE may not be the folder of your source file.
  • Overwriting by accident. A plain ofstream erases the existing file; use std::ios::app to add.
  • Reading a file while it is still open for writing in the same program. Unflushed data may not be in the file yet; close the writer first, for example by ending its block.

Key points

  • std::ofstream writes, std::ifstream reads and std::fstream does both; they close files automatically.
  • Write with << or C++23 std::println(stream, ...); read with std::getline or >>.
  • Loop on the read itself: while (std::getline(in, line)) or while (in >> value).
  • std::ios::app appends; std::ios::binary stops newline translation.
  • std::istringstream splits a line into fields; std::filesystem manages paths, folders and files.

Exercise

Write a program for a cricket club in Pune. It should write three lines to players.csv in the form name,matches,runs (Rohan,12,420 and Isha,10,515 and Farhan,8,198), then read the file back, print each player's batting average (runs divided by matches, two decimals), and finally print the name of the player with the highest average.

Show solution
C++
#include <fstream>
#include <print>
#include <sstream>
#include <string>

int main() {
    {
        std::ofstream out("players.csv");
        out << "Rohan,12,420\n";
        out << "Isha,10,515\n";
        out << "Farhan,8,198\n";
    }

    std::ifstream in("players.csv");
    std::string line;
    std::string best_name;
    double best_average = -1.0;

    while (std::getline(in, line)) {
        std::istringstream fields(line);
        std::string name, matches, runs;
        std::getline(fields, name, ',');
        std::getline(fields, matches, ',');
        std::getline(fields, runs, ',');

        double average = std::stod(runs) / std::stoi(matches);
        std::println("{}: {:.2f}", name, average);
        if (average > best_average) {
            best_average = average;
            best_name = name;
        }
    }
    std::println("Best average: {}", best_name);
}
Output
Rohan: 35.00
Isha: 51.50
Farhan: 24.75
Best average: Isha

std::stod makes the division use decimals. The writing block ends before reading starts, so all three lines are safely in the file.

Quick Quiz

1. What happens when you open an existing file with a plain std::ofstream out("data.txt");?

2. Which loop correctly reads every line of a file?

3. Why don't you need to call close() on a file stream?

4. Which class splits a string into fields as if it were a stream?

Learn C++ with a mentor

Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call