File Streams
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.
#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 hereSaved 3 records
After the program runs, marks.txt contains:
Aarav 82
Priya 95
Kabir 77You 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.
#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);
}
}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.
#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);
}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.
#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);
}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 mode | Meaning |
|---|---|
(default for ofstream) | Create the file or replace its content |
std::ios::app | Write at the end, keeping existing content |
std::ios::binary | No newline translation; for images and other non-text data |
std::ios::in with std::fstream | Open 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.
#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));
}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
ofstreamerases the existing file; usestd::ios::appto 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::ofstreamwrites,std::ifstreamreads andstd::fstreamdoes both; they close files automatically.- Write with
<<or C++23std::println(stream, ...); read withstd::getlineor>>. - Loop on the read itself:
while (std::getline(in, line))orwhile (in >> value). std::ios::appappends;std::ios::binarystops newline translation.std::istringstreamsplits a line into fields;std::filesystemmanages 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
#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);
}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");?
The default mode truncates the file; use std::ios::app to append.
2. Which loop correctly reads every line of a file?
Looping on getline stops exactly when a read fails; the eof() pattern processes the last line twice.
3. Why don't you need to call close() on a file stream?
File streams are RAII objects: the file closes when the stream goes out of scope.
4. Which class splits a string into fields as if it were a stream?
std::istringstream reads from a string, so std::getline(fields, part, ',') can split it at commas.
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
