Files and Streams
File streams work just like std::cin and std::cout: std::ofstream writes to a file with <<, std::ifstream reads with >> and std::getline. Files close automatically when the stream object goes out of scope. The <filesystem> library handles paths, folders and file information.
Writing a file
#include <fstream> #include <iostream> int main() { std::ofstream out{"shopping.txt"}; if (!out) { std::cerr << "could not open shopping.txt for writing\n"; return 1; } out << "eggs,12\n"; out << "milk,2\n"; out << "bread,1\n"; std::cout << "saved\n"; } // out is closed (and flushed) here
saved
A stream converts to false if opening failed. Opening an ofstream replaces any existing file; pass std::ios::app as a second argument to append instead.
Reading line by line
apples,30,0.45 bananas,24,0.30 cherries,200,0.05 dates,15,0.80
#include <fstream> #include <iomanip> #include <iostream> #include <sstream> #include <string> int main() { std::ifstream in{"inventory.csv"}; if (!in) { std::cerr << "cannot open inventory.csv\n"; return 1; } std::string line; double total = 0; while (std::getline(in, line)) { std::istringstream fields{line}; std::string name; int quantity = 0; double price = 0; char comma1 = 0; std::getline(fields, name, ','); if (!(fields >> quantity >> comma1 >> price) || comma1 != ',') { std::cerr << "bad line: " << line << '\n'; continue; } std::cout << std::left << std::setw(10) << name << std::right << std::setw(5) << quantity << " x " << std::fixed << std::setprecision(2) << price << '\n'; total += quantity * price; } std::cout << "Total stock value: " << total << '\n'; }
apples 30 x 0.45 bananas 24 x 0.30 cherries 200 x 0.05 dates 15 x 0.80 Total stock value: 42.70
Reading a whole line, then parsing it with a std::istringstream, keeps one bad line from derailing the rest — the same approach as for keyboard input.
Reading a whole file
#include <fstream> #include <iostream> #include <sstream> #include <string> std::string read_file(const std::string& path) { std::ifstream in{path}; std::ostringstream contents; contents << in.rdbuf(); return contents.str(); } int main() { { std::ofstream{"note.txt"} << "line one\nline two\n"; } std::string text = read_file("note.txt"); std::cout << text.size() << " characters:\n" << text; }
18 characters: line one line two
Words and numbers
Ana 82 Ben 91 Chloe 77
#include <fstream> #include <iostream> #include <string> int main() { std::ifstream in{"scores.txt"}; std::string name; int score = 0, count = 0, sum = 0; while (in >> name >> score) { sum += score; ++count; } std::cout << count << " students, average " << static_cast<double>(sum) / count << '\n'; }
3 students, average 83.3333
Binary files
Open with std::ios::binary and use write/read for raw bytes. As in C, writing structs directly ties the file to your compiler's layout and byte order — fine for caches, not for files shared between systems:
#include <fstream> #include <iostream> #include <vector> int main() { std::vector<double> readings{21.5, 22.0, 21.75}; { std::ofstream out{"readings.bin", std::ios::binary}; out.write(reinterpret_cast<const char*>(readings.data()), static_cast<std::streamsize>(readings.size() * sizeof(double))); } std::vector<double> loaded(3); std::ifstream in{"readings.bin", std::ios::binary}; in.read(reinterpret_cast<char*>(loaded.data()), static_cast<std::streamsize>(loaded.size() * sizeof(double))); std::cout << "read " << in.gcount() << " bytes: " << loaded[0] << ' ' << loaded[1] << ' ' << loaded[2] << '\n'; }
read 24 bytes: 21.5 22 21.75
std::filesystem
C++17's <filesystem> works with paths, folders and file metadata portably:
#include <filesystem> #include <fstream> #include <iostream> namespace fs = std::filesystem; int main() { fs::path dir = "reports"; fs::create_directories(dir / "2026"); std::ofstream{dir / "2026" / "march.txt"} << "sales: 12400\n"; std::ofstream{dir / "2026" / "april.txt"} << "sales: 13950\n"; fs::path p = dir / "2026" / "march.txt"; std::cout << "file: " << p.filename() << ", stem: " << p.stem() << ", extension: " << p.extension() << '\n'; std::cout << "exists: " << std::boolalpha << fs::exists(p) << ", size: " << fs::file_size(p) << " bytes\n"; int count = 0; for (const auto& entry : fs::directory_iterator(dir / "2026")) { if (entry.is_regular_file()) ++count; } std::cout << count << " files in " << (dir / "2026").string() << '\n'; fs::remove_all(dir); }
file: "march.txt", stem: "march", extension: ".txt" exists: true, size: 13 bytes 2 files in reports/2026
/ joins path parts with the right separator for the platform. Directory iteration order is unspecified, so sort entries if order matters.
Try it yourself
Copy a text file line by line, numbering the lines.
Roses are red, templates are tricky, RAII cleans up so leaks don't get sticky.
1 Roses are red, 2 templates are tricky, 3 RAII cleans up 4 so leaks don't get sticky.