File Handling
C++ file I/O uses stream classes from <fstream>. They work the same way as cout/cin — you write with << and read with >> or getline. RAII means files close automatically when streams go out of scope. C++17 adds <filesystem> for directory traversal, file metadata, and path manipulation.
Opening and Closing Files
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
// ofstream — write only
ofstream out("output.txt");
if (!out.is_open()) {
cerr << "Failed to open output.txt\n";
return 1;
}
out << "Hello, File!\n";
out << "Line 2\n";
out.close(); // Optional — closes when out goes out of scope
// ifstream — read only
ifstream in("output.txt");
if (!in) { // Shorthand for !in.is_open()
cerr << "Cannot read output.txt\n";
return 1;
}
string line;
while (getline(in, line)) {
cout << line << "\n";
}
// File closes automatically when 'in' goes out of scope
return 0;
}Open Modes
#include <fstream>
using namespace std;
// Open modes (can be combined with |)
ofstream f1("file.txt"); // Default: out | trunc (overwrite)
ofstream f2("file.txt", ios::app); // Append to existing
ofstream f3("file.txt", ios::out | ios::trunc);// Overwrite (explicit)
ofstream f4("file.bin", ios::out | ios::binary);// Binary mode
// fstream — read AND write
fstream rw("file.txt", ios::in | ios::out);
// Mode flags
// ios::in — read
// ios::out — write
// ios::app — append (seek to end before each write)
// ios::ate — seek to end immediately after opening
// ios::trunc — truncate file to zero length on open
// ios::binary — binary mode (no newline translation)Reading Text Files
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
// Read entire file into a string
string readFile(const string& path) {
ifstream f(path);
if (!f) throw runtime_error("Cannot open: " + path);
ostringstream ss;
ss << f.rdbuf(); // Stream entire file through stringstream
return ss.str();
}
// Read file line by line into vector
vector<string> readLines(const string& path) {
ifstream f(path);
if (!f) throw runtime_error("Cannot open: " + path);
vector<string> lines;
string line;
while (getline(f, line)) {
lines.push_back(line);
}
return lines;
}
// Read integers from file
vector<int> readIntegers(const string& path) {
ifstream f(path);
vector<int> nums;
int n;
while (f >> n) { // Reads whitespace-separated integers
nums.push_back(n);
}
return nums;
}
int main() {
// Create a test file
{
ofstream out("test.txt");
out << "Alice 90\nBob 85\nCharlie 78\n";
}
// Read line by line
for (const auto& line : readLines("test.txt")) {
cout << line << "\n";
}
// Parse name-score pairs
ifstream f("test.txt");
string name;
int score;
while (f >> name >> score) {
cout << name << " got " << score << "\n";
}
return 0;
}Writing Text Files
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
struct Record {
string name;
int age;
double salary;
};
int main() {
vector<Record> records = {
{"Alice", 30, 75000.0},
{"Bob", 25, 62000.0},
{"Charlie", 35, 89000.0},
};
// Write CSV
ofstream csv("employees.csv");
csv << "Name,Age,Salary\n";
for (const auto& r : records) {
csv << r.name << "," << r.age << "," << fixed << setprecision(2) << r.salary << "\n";
}
// Append to log file
ofstream log("app.log", ios::app);
log << "[2025-03-01 10:30:00] Application started\n";
log << "[2025-03-01 10:30:05] Processing 3 records\n";
// Check stream state
if (csv.fail()) {
cerr << "Write error!\n";
}
return 0;
}Binary Files
#include <iostream>
#include <fstream>
using namespace std;
struct Pixel {
uint8_t r, g, b, a;
};
int main() {
// Write binary data
vector<Pixel> image(256);
for (int i = 0; i < 256; i++) {
image[i] = {(uint8_t)i, (uint8_t)(255-i), 128, 255};
}
ofstream out("image.bin", ios::binary);
out.write(reinterpret_cast<const char*>(image.data()),
image.size() * sizeof(Pixel));
// Read binary data
vector<Pixel> loaded(256);
ifstream in("image.bin", ios::binary);
in.read(reinterpret_cast<char*>(loaded.data()),
loaded.size() * sizeof(Pixel));
cout << "Loaded " << in.gcount() << " bytes\n";
cout << "First pixel: "
<< (int)loaded[0].r << " "
<< (int)loaded[0].g << " "
<< (int)loaded[0].b << "\n"; // 0 255 128
// Write a struct directly
struct Header {
char magic[4] = {'B','I','N','0'};
uint32_t version = 1;
uint64_t dataSize = 0;
};
Header h;
h.dataSize = 256 * sizeof(Pixel);
ofstream hout("data.bin", ios::binary);
hout.write(reinterpret_cast<const char*>(&h), sizeof(h));
return 0;
}Warning: Binary serialisation is platform-dependent — endianness and struct padding may differ between machines. For portable serialisation, use text formats (JSON, CSV) or a cross-platform library like Protocol Buffers.
File Seeking
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// Create a file with known content
{
ofstream out("seek_test.txt");
out << "Hello, World! This is a test.";
}
fstream f("seek_test.txt", ios::in | ios::out);
// Get file size
f.seekg(0, ios::end); // Seek to end
streamsize size = f.tellg(); // Position = file size
cout << "File size: " << size << " bytes\n"; // 30
// Read from a specific position
f.seekg(7, ios::beg); // Seek to offset 7 from beginning
string word;
f >> word;
cout << "Word at offset 7: " << word << "\n"; // World!
// seekg (read position) vs seekp (write position)
f.seekp(0, ios::beg); // Seek write position to start
f.write("Goodbye", 7); // Overwrite first 7 characters
f.seekg(0, ios::beg); // Back to start for reading
string content;
getline(f, content);
cout << content << "\n"; // Goodbye, World! This is a test.
return 0;
}Error Handling with Streams
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// Enable exceptions on stream failure
ifstream f;
f.exceptions(ios::failbit | ios::badbit);
try {
f.open("nonexistent.txt");
} catch (const ios_base::failure& e) {
cerr << "File error: " << e.what() << "\n";
}
// Manual error checking
ifstream f2("existing.txt");
if (!f2) {
// Check individual error bits
if (f2.eof()) cerr << "End of file\n";
if (f2.fail()) cerr << "Logical error\n";
if (f2.bad()) cerr << "I/O error\n";
f2.clear(); // Reset error flags
}
return 0;
}C++17 <filesystem>
#include <iostream>
#include <filesystem>
#include <fstream>
using namespace std;
namespace fs = filesystem;
int main() {
// Path operations
fs::path p = "/home/user/projects/myapp";
cout << p.filename() << "\n"; // myapp
cout << p.parent_path() << "\n";// /home/user/projects
cout << p.extension() << "\n"; // (empty for directories)
fs::path f = "/home/user/data.csv";
cout << f.stem() << "\n"; // data
cout << f.extension() << "\n"; // .csv
cout << (f.parent_path() / "output.txt") << "\n"; // /home/user/output.txt
// Check existence and properties
if (fs::exists("/tmp")) {
cout << "is_dir: " << fs::is_directory("/tmp") << "\n"; // 1
cout << "is_file: " << fs::is_regular_file("/tmp") << "\n"; // 0
}
// Create directories
fs::create_directories("output/data/processed");
// Directory iteration
for (const auto& entry : fs::directory_iterator("output")) {
cout << entry.path() << " ";
cout << (entry.is_directory() ? "(dir)" : "(file)") << "\n";
}
// Recursive directory iteration
for (const auto& entry : fs::recursive_directory_iterator(".")) {
if (entry.path().extension() == ".cpp") {
cout << entry.path() << " ("
<< fs::file_size(entry.path()) << " bytes)\n";
}
}
// Copy, move, rename, delete
fs::copy("source.txt", "dest.txt", fs::copy_options::overwrite_existing);
fs::rename("old.txt", "new.txt");
fs::remove("temp.txt");
fs::remove_all("output/data/processed"); // Remove recursively
return 0;
}Practical Example: CSV Database
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
struct Employee {
int id;
string name;
string department;
double salary;
string toCsv() const {
return to_string(id) + "," + name + "," + department + ","
+ to_string(salary);
}
static Employee fromCsv(const string& line) {
istringstream ss(line);
string field;
Employee e;
getline(ss, field, ','); e.id = stoi(field);
getline(ss, e.name, ',');
getline(ss, e.department, ',');
getline(ss, field, ','); e.salary = stod(field);
return e;
}
};
class EmployeeDB {
string filename;
vector<Employee> employees;
int nextId;
public:
EmployeeDB(const string& file) : filename(file), nextId(1) {
load();
}
void load() {
ifstream f(filename);
if (!f) return; // New file — no data yet
string line;
getline(f, line); // Skip header
while (getline(f, line)) {
if (!line.empty()) {
auto e = Employee::fromCsv(line);
employees.push_back(e);
nextId = max(nextId, e.id + 1);
}
}
cout << "Loaded " << employees.size() << " employees\n";
}
void save() const {
ofstream f(filename, ios::trunc);
f << "ID,Name,Department,Salary\n";
for (const auto& e : employees) f << e.toCsv() << "\n";
cout << "Saved " << employees.size() << " employees\n";
}
void add(const string& name, const string& dept, double salary) {
employees.push_back({nextId++, name, dept, salary});
}
void list() const {
cout << left << setw(4) << "ID"
<< setw(15) << "Name"
<< setw(12) << "Dept"
<< "Salary\n" << string(40, '-') << "\n";
for (const auto& e : employees) {
cout << setw(4) << e.id
<< setw(15) << e.name
<< setw(12) << e.department
<< fixed << setprecision(0) << e.salary << "\n";
}
}
};
int main() {
EmployeeDB db("employees.csv");
db.add("Alice", "Engineering", 85000);
db.add("Bob", "Design", 70000);
db.add("Charlie", "Marketing", 65000);
db.list();
db.save();
return 0;
}Summary
ifstream,ofstream,fstreaminherit from standard streams —<<,>>,getlineall work identically- Files are automatically closed when the stream goes out of scope — RAII in action
- Open modes:
ios::in(read),ios::out(write),ios::app(append),ios::binary,ios::trunc seekg()moves the read position;seekp()moves the write position;tellg()/tellp()return the current position- Binary files use
read()/write()withreinterpret_cast<char*>— beware of endianness and padding - Check stream state with
f.fail(),f.bad(),f.eof()— or enable exceptions withf.exceptions(...) - C++17
<filesystem>providespath,directory_iterator,create_directories,copy,rename,remove_all
Next, we'll explore multithreading — running code in parallel with std::thread, mutexes, and futures.