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:
| Class | Purpose |
|---|---|
ifstream | Input file stream — read from files |
ofstream | Output file stream — write to files |
fstream | Both 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:
| Mode | Meaning |
|---|---|
ios::in | Open for reading |
ios::out | Open for writing (default for ofstream) |
ios::app | Append to end of file |
ios::trunc | Truncate (clear) file on open (default for ofstream) |
ios::binary | Open 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.
Related Articles
- C++ Variables and Data Types — understand the types you will be reading and writing to files.
- C++ String Handling: std::string & string_view Guide — strings are the most common data type when working with files.
- C++ Functions Tutorial — organise your file I/O logic into clean, reusable functions.
- Exception Handling in C++: try, catch & throw — handle file errors gracefully with exceptions.
- C++ Cheat Sheet: Quick Reference for Syntax, STL, and OOP — a quick reference for file stream syntax and modes.