File I/O in C++
Almost every real-world C++ program needs to persist data beyond a single run — reading configuration, logging results, importing student records, or writing reports. In C++, file input/output is handled through the <fstream> header, which provides three classes that mirror std::cin and std::cout but operate on files instead of the console.
For a software engineer at an Indian company — whether you are building an attendance system for a school in Pune, a payroll tool for a BPO in Gurgaon, or a data pipeline at Flipkart — understanding file I/O is a foundational skill.
The Three File Stream Classes
| Class | Header | Purpose |
|---|---|---|
std::ifstream | <fstream> | Read from a file |
std::ofstream | <fstream> | Write to a file |
std::fstream | <fstream> | Both read and write |
All three inherit from base classes in <iostream>, so the operators you already know (>>, <<, getline) work exactly the same way.
Opening Files and File Modes
You can open a file either in the constructor or by calling .open().
#include <fstream>
#include <iostream>
int main() {
// Method 1: open in constructor
std::ifstream inFile("data.txt");
// Method 2: open explicitly
std::ofstream outFile;
outFile.open("results.txt");
}
File Opening Modes
Modes are bit flags from std::ios. You can combine them with the bitwise OR operator |.
| Mode | Meaning |
|---|---|
std::ios::in | Open for reading (default for ifstream) |
std::ios::out | Open for writing; truncates existing file (default for ofstream) |
std::ios::app | Append; writes go to the end of file |
std::ios::ate | Seek to end immediately after opening |
std::ios::trunc | Truncate file to zero length on open |
std::ios::binary | Open in binary mode (no newline translation) |
// Append to a log file without overwriting
std::ofstream logFile("app.log", std::ios::app);
// Open for both reading and writing in binary mode
std::fstream dbFile("records.bin", std::ios::in | std::ios::out | std::ios::binary);
Error Handling — is_open() and fail()
Opening a file can fail if the path does not exist, permissions are denied, or the disk is full. Always check before operating on the stream.
std::ifstream inFile("students.csv");
if (!inFile.is_open()) {
std::cerr << "Error: Could not open students.csv\n";
return 1;
}
// After reading, check for errors
if (inFile.fail() && !inFile.eof()) {
std::cerr << "Error: A read error occurred\n";
}
Stream State Flags
| Flag | Method | Meaning |
|---|---|---|
goodbit | .good() | No errors |
eofbit | .eof() | End of file reached |
failbit | .fail() | Logical error (bad format) |
badbit | .bad() | Unrecoverable I/O error |
Reading Text Files
Reading Word by Word
#include <fstream>
#include <iostream>
#include <string>
int main() {
std::ifstream file("words.txt");
if (!file.is_open()) {
std::cerr << "Cannot open file\n";
return 1;
}
std::string word;
while (file >> word) {
std::cout << word << "\n";
}
file.close(); // optional — destructor closes automatically
}
Reading Line by Line with getline
When lines contain spaces (like full names or addresses), use std::getline:
#include <fstream>
#include <iostream>
#include <string>
int main() {
std::ifstream file("addresses.txt");
std::string line;
while (std::getline(file, line)) {
std::cout << "Line: " << line << "\n";
}
}
Writing to Files
#include <fstream>
#include <iostream>
int main() {
std::ofstream outFile("report.txt");
if (!outFile) { // implicit bool conversion
std::cerr << "Failed to create report.txt\n";
return 1;
}
outFile << "Quarterly Sales Report\n";
outFile << "Region: Maharashtra\n";
outFile << "Total Revenue: Rs. 45,20,000\n";
// File is flushed and closed when outFile goes out of scope
}
Reading and Writing CSV Data
CSV (Comma-Separated Values) is the most common format for structured data in India's enterprise software — from HRMS exports to government portals.
Parsing a CSV Line
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
std::vector<std::string> splitCSV(const std::string& line) {
std::vector<std::string> fields;
std::stringstream ss(line);
std::string field;
while (std::getline(ss, field, ',')) {
fields.push_back(field);
}
return fields;
}
int main() {
std::ifstream file("students.csv");
if (!file.is_open()) {
std::cerr << "Cannot open students.csv\n";
return 1;
}
std::string line;
std::getline(file, line); // skip header row
while (std::getline(file, line)) {
auto fields = splitCSV(line);
if (fields.size() >= 3) {
std::string name = fields[0];
int roll = std::stoi(fields[1]);
double marks = std::stod(fields[2]);
std::cout << name << " | Roll: " << roll << " | Marks: " << marks << "\n";
}
}
}
Binary File I/O
Binary mode stores data in its raw memory representation, which is compact and fast — ideal for large datasets where you do not need human-readable files.
#include <fstream>
#include <iostream>
struct Employee {
char name[50];
int employeeId;
double salary;
};
int main() {
Employee emp = {"Ravi Kumar", 1042, 95000.0};
// Write binary
std::ofstream out("employee.dat", std::ios::binary);
out.write(reinterpret_cast<const char*>(&emp), sizeof(emp));
out.close();
// Read binary
Employee emp2{};
std::ifstream in("employee.dat", std::ios::binary);
in.read(reinterpret_cast<char*>(&emp2), sizeof(emp2));
in.close();
std::cout << "Name: " << emp2.name << "\n";
std::cout << "ID: " << emp2.employeeId << "\n";
std::cout << "Salary: Rs." << emp2.salary << "\n";
}
Note: Binary files are not portable across machines with different endianness or compilers with different struct padding rules. For portable serialisation, prefer text or a serialisation library.
Worked Example: Student CSV Processor
This program reads student records from a CSV file, computes the class average, identifies pass/fail based on Indian university norms (40% passing), and writes a results file.
Input file (students.csv):
Name,RollNo,Marks
Aarav Sharma,101,78
Priya Nair,102,35
Rohan Desai,103,92
Fatima Khan,104,41
Arjun Mehta,105,28
Sneha Patel,106,65
Program:
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
struct Student {
std::string name;
int rollNo;
double marks;
};
std::vector<std::string> splitCSV(const std::string& line) {
std::vector<std::string> fields;
std::stringstream ss(line);
std::string field;
while (std::getline(ss, field, ',')) {
fields.push_back(field);
}
return fields;
}
int main() {
// ---- READ ----
std::ifstream inFile("students.csv");
if (!inFile.is_open()) {
std::cerr << "Error: Cannot open students.csv\n";
return 1;
}
std::vector<Student> students;
std::string line;
std::getline(inFile, line); // discard header
while (std::getline(inFile, line)) {
if (line.empty()) continue;
auto fields = splitCSV(line);
if (fields.size() < 3) continue;
Student s;
s.name = fields[0];
s.rollNo = std::stoi(fields[1]);
s.marks = std::stod(fields[2]);
students.push_back(s);
}
inFile.close();
if (students.empty()) {
std::cerr << "No student records found.\n";
return 1;
}
// ---- PROCESS ----
double total = 0.0;
for (const auto& s : students) {
total += s.marks;
}
double average = total / students.size();
// ---- WRITE ----
std::ofstream outFile("results.txt");
if (!outFile.is_open()) {
std::cerr << "Error: Cannot create results.txt\n";
return 1;
}
outFile << "=== Examination Results ===\n";
outFile << std::left
<< std::setw(20) << "Name"
<< std::setw(10) << "Roll No"
<< std::setw(10) << "Marks"
<< std::setw(10) << "Status" << "\n";
outFile << std::string(50, '-') << "\n";
int passed = 0, failed = 0;
for (const auto& s : students) {
std::string status = (s.marks >= 40.0) ? "PASS" : "FAIL";
if (s.marks >= 40.0) ++passed; else ++failed;
outFile << std::left
<< std::setw(20) << s.name
<< std::setw(10) << s.rollNo
<< std::setw(10) << std::fixed << std::setprecision(1) << s.marks
<< std::setw(10) << status << "\n";
}
outFile << std::string(50, '-') << "\n";
outFile << "Class Average : " << std::fixed << std::setprecision(2) << average << "\n";
outFile << "Total Passed : " << passed << "\n";
outFile << "Total Failed : " << failed << "\n";
outFile.close();
std::cout << "Results written to results.txt\n";
std::cout << "Class average: " << average << "\n";
return 0;
}
Output in results.txt:
=== Examination Results ===
Name Roll No Marks Status
--------------------------------------------------
Aarav Sharma 101 78.0 PASS
Priya Nair 102 35.0 FAIL
Rohan Desai 103 92.0 PASS
Fatima Khan 104 41.0 PASS
Arjun Mehta 105 28.0 FAIL
Sneha Patel 106 65.0 PASS
--------------------------------------------------
Class Average : 56.50
Total Passed : 4
Total Failed : 2
Common Pitfalls
1. Not checking if the file opened successfully
Accessing a stream that failed to open causes silent data corruption or reads of empty strings. Always call is_open() or check the stream's boolean value before proceeding.
2. Using >> when you need getline
The >> operator stops at whitespace. For full lines or fields with spaces, use std::getline. Mixing the two without a file.ignore() call leaves a stray newline in the buffer, causing getline to read an empty line immediately.
int n;
file >> n;
file.ignore(); // discard the leftover '\n'
std::getline(file, line); // now reads correctly
3. Forgetting std::ios::binary for binary files
On Windows, text mode converts \n to \r\n during writes. For binary data this corrupts the file. Always use std::ios::binary when writing raw structs or images.
4. Using reinterpret_cast for non-trivial types
write/read with reinterpret_cast only works for plain-old-data (POD) types. Writing a std::string or std::vector this way writes a pointer, not the data. Use text serialisation for complex objects.
5. Assuming files close automatically in error paths
While the destructor closes the file, calling close() explicitly is good practice when you need to be sure the data is flushed before the next operation reads the file.
Practice Exercises
-
Write a program that reads a text file and counts the number of lines, words, and characters. Print a summary similar to the Unix
wccommand. -
Create a program that maintains a simple to-do list stored in
todo.txt. Support two operations:--add "task description"(append a new task) and--list(print all tasks with line numbers). -
Read a CSV file containing product inventory (
name,quantity,price) for a kirana store. Write a new CSV containing only items where the quantity is below 10, as a low-stock alert. -
Write a binary file containing an array of 5
doublevalues representing daily temperature readings in Jaipur. Then write a separate program that reads the binary file and computes the weekly average. -
Write a program that reads a log file line by line and writes every line containing the word "ERROR" to a separate
errors.txtfile. -
Extend the student CSV example to compute the highest and lowest marks, and add a line at the top of
results.txtdisplaying the topper's name and score.
Summary
- Use
std::ifstreamfor reading,std::ofstreamfor writing, andstd::fstreamfor both. - File modes like
std::ios::app,std::ios::binary, andstd::ios::trunccontrol how the file is opened. - Always verify the file opened successfully with
is_open()or the stream's boolean conversion before reading or writing. - Use
std::getlinefor line-by-line reading; use astd::stringstreamwith a delimiter to parse CSV fields. - Binary mode (
std::ios::binary) stores raw memory representation — compact but platform-dependent. - Use
reinterpret_castwithwrite/readonly for POD (plain-old-data) structs. - Mixing
>>andgetlinerequiresfile.ignore()to clear the leftover newline from the buffer. - File streams flush and close automatically when they go out of scope, but explicit
close()is clearer and ensures data is written before the next read.