Lesson • Intermediate Track
Files & Streams
By the end of this lesson you'll be able to write data to a file, read it back line by line or token by token, choose the right file mode, and parse text in memory with std::stringstream — the same stream skills power files, the console, and strings in C++.
What You'll Learn
- Write text files with std::ofstream and the << operator
- Read files back with std::ifstream, >>, and std::getline
- Use std::fstream and file modes (ios::app, ios::binary)
- Always check is_open() and detect end-of-file (EOF) correctly
- Parse strings in memory with std::stringstream
- Close files properly and rely on RAII
cout, std::string, and a basic while loop. A heads-up for this lesson: online compilers usually have no writable filesystem, so the file examples show expected output in comments and are meant to be run on your own machine. The "Your Turn" exercises use std::stringstream, which runs anywhere — including the editor here.💡 Real-World Analogy
Think of a stream as a conveyor belt for characters. cout is a belt running out to your screen; cin is a belt running in from the keyboard. A file stream is the exact same belt — it just connects to a file on disk instead. Because every belt works the same way, the << ("push onto the belt") and >> ("take off the belt") operators you already know for the console work unchanged on files and on strings. Learn one, and you've learned all three.
1. Writing a File with ofstream
std::ofstream (output file stream) opens a file for writing. You give it a filename, then push data onto it with << — exactly like cout. The golden rule: always check is_open() first, because a wrong path or a read-only folder fails silently otherwise. Read this worked example, then run it locally and open scores.txt to see the result.
Worked example: write a text file
Runs on your own machine — online sandboxes may lack a writable filesystem. Read every comment.
#include <iostream>
#include <fstream> // file streams live here: ofstream, ifstream, fstream
using namespace std;
int main() {
// ofstream = "output file stream" -> opens a file for WRITING.
// If the file does not exist it is created; if it exists it is OVERWRITTEN.
ofstream out("scores.txt");
// ALWAYS check the file actually opened before you use it.
if (!out.is_open()) {
cout << "Could not open scores.txt for writing" << endl;
return 1;
...2. Reading a File with ifstream
std::ifstream (input file stream) opens a file for reading. You have two tools: std::getline(in, line) grabs a whole line at a time, while in >> value reads one whitespace-separated token and converts its type for you. Both return the stream itself, which becomes "false" at end-of-file (EOF) — so they're perfect loop conditions that stop on their own.
Worked example: read a file two ways
getline for whole lines vs >> for tokens. Run locally after Section 1.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
// ifstream = "input file stream" -> opens a file for READING.
ifstream in("scores.txt");
if (!in.is_open()) {
cout << "scores.txt not found" << endl;
return 1;
}
// Pattern 1: read WHOLE LINES with getline.
// getline(stream, line) returns the stream, which is "false" at end-of-file,
// so the loop stops automatically when there is nothing left to read.
s
...3. File Modes: app and binary
By default ofstream overwrites a file. Pass a mode flag as the second argument to change that. std::ios::app means append — new writes go to the end and existing content is kept (ideal for log files). std::ios::binary writes raw bytes with no text formatting, which you need for .write()/.read() on structs. std::fstream opens a file for reading and writing at once.
Worked example: append & binary modes
ios::app keeps existing content; ios::binary copies raw bytes. Run locally.
#include <iostream>
#include <fstream>
using namespace std;
struct Point { int x; int y; }; // a fixed-size record (8 bytes here)
int main() {
// ios::app -> APPEND. Writes go to the END; existing content is kept.
ofstream log("log.txt", ios::app);
log << "user logged in" << endl; // added after whatever was already there
log.close();
// ios::binary -> write RAW BYTES, no text formatting / newline translation.
Point p{3, 4};
ofstream bout("point.bin", ios::bin
...Heads-up: binary files are not portable across machines — endianness and struct padding differ between architectures. For data you'll share, prefer a text format (CSV, JSON) or a serialization library.
4. Parsing in Memory with stringstream
A std::stringstream is a stream backed by a std::string instead of a file. std::istringstream lets you read a string with getline and >> (perfect for splitting a CSV line into fields); std::ostringstream lets you build a string with << and pull the result out with .str(). Because it's all in memory, it runs anywhere — so the exercises below work right here in the editor.
Your turn. The program below is almost complete — fill in the blanks marked ___ using the // 👉 hints, then run it.
🎯 Your turn: split a CSV line
Fill in the ___ blanks, then check your output against the expected line.
#include <iostream>
#include <sstream> // istringstream = "read a string as if it were a file"
#include <string>
using namespace std;
int main() {
// 🎯 YOUR TURN — fill in the blanks marked ___ then press "Try it Yourself".
// No real file needed: istringstream lets us PRACTISE parsing in memory.
string csv = "Alice,London,30";
// istringstream wraps the string so we can use getline / >> on it.
istringstream iss(csv);
string name, city, ageText;
// getline(str
...One more. This time you'll read numbers with >> and build a report string with ostringstream. Fill in the two blanks:
🎯 Your turn: build a report string
Append the total, then extract the finished string with .str().
#include <iostream>
#include <sstream> // ostringstream builds a string; istringstream reads one
#include <string>
#include <vector>
using namespace std;
int main() {
// 🎯 YOUR TURN — parse some numbers, then BUILD a report string.
string data = "10 25 5"; // three numbers separated by spaces
istringstream in(data);
// Read tokens with >> (it splits on whitespace for you).
int total = 0, n;
vector<int> nums;
while (in >> n) {
nums.push_back(n);
...Common Errors (and the fix)
- Not checking
is_open(): if the path is wrong or the folder is read-only, the open fails silently and your writes vanish. Always guard withif (!stream.is_open()) { /* handle it */ }before reading or writing. - Mixing
>>andgetline:>>leaves the trailing newline in the buffer, so the nextgetlinereturns an empty line. After reading a number with>>, callin.ignore()(orstd::ws) beforegetline. - Forgetting
ios::binary: calling.write()/.read()in text mode lets newline translation corrupt your bytes on Windows. Open withofstream out(name, ios::binary)for any non-text data. - Not closing / not relying on RAII: data sits in a buffer until the stream is flushed. If you read the file before the writer's
close()(or before it goes out of scope), it can look empty. Call.close()or let the stream's scope end first. - Looping on
!in.eof(): EOF is set after a failed read, so this processes the last line twice. Loop on the read itself instead:while (getline(in, line))orwhile (in >> x).
📋 Quick Reference
| Task | Code |
|---|---|
| Open file to write | ofstream out("f.txt"); |
| Open file to read | ifstream in("f.txt"); |
| Read & write | fstream io("f.txt"); |
| Append mode | ofstream(f, ios::app) |
| Binary mode | ifstream(f, ios::binary) |
| Check it opened | if (in.is_open()) |
| Read a line | getline(in, line) |
| Read a token | in >> word |
| Split on a char | getline(ss, f, ',') |
| Build a string | oss << x; oss.str() |
| Close the file | stream.close(); |
Frequently Asked Questions
Q: Why does my online program say it can't open or find the file?
Many online compilers (including this one) run in a sandbox with no writable or persistent filesystem, so ofstream may fail and a file you 'wrote' won't exist on the next run. That's exactly why the practice exercises here use std::stringstream, which works entirely in memory. The file examples run perfectly on your own machine — install a compiler like g++ and run them locally.
Q: When should I use getline() instead of >> ?
Use >> to read one whitespace-separated token at a time (a word or a number) with automatic type conversion. Use getline() when you want a whole line, including the spaces inside it — for example a full name or address. getline() also takes an optional delimiter, so getline(s, field, ',') is the easy way to split CSV data.
Q: Do I really need to call .close()?
Not strictly — file streams use RAII, so the destructor closes the file automatically when the stream goes out of scope. But calling .close() explicitly flushes the buffer to disk immediately and frees the file so another part of your program (or another program) can open it. It also makes your intent obvious to the next reader.
Q: What does ios::binary actually change?
It turns off text translation. On Windows, text mode rewrites '\n' as '\r\n' on write and back on read; binary mode leaves every byte exactly as-is. You need ios::binary whenever you call .write()/.read() with reinterpret_cast on structs or non-text data, otherwise stray byte translation can corrupt it.
Q: Why is istringstream useful if it's not a file?
Because the stream interface is the same. Once you can parse an istringstream you can parse a file — the only difference is where the characters come from. It's also perfect for splitting one line you've already read (with getline) into fields, and for converting text to numbers safely.
Mini-Challenge: Average a Row of Scores
No blanks this time — just a brief and an outline. You'll parse a comma-separated line entirely in memory with istringstream, so it runs right here. Build it, run it, and check your output against the example in the comments.
🎯 Mini-Challenge: average the scores
Parse the CSV row with istringstream and print the average.
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main() {
// 🎯 MINI-CHALLENGE: average a row of scores
// The data is a comma-separated line (pretend it came from a CSV file):
string row = "Maths,72,68,90,85";
//
// 1. Wrap "row" in an istringstream.
// 2. getline(..., ',') the first field into a "subject" string.
// 3. Loop getline(..., token, ',') over the rest; convert each with stoi(token),
// add to a running sum and coun
...Pro Tips
- 💡 Prefer the read as your loop test:
while (getline(in, line))beats checkingeof()— it never processes a phantom last line. - 💡 Read a line, then parse it:
getlinethe whole line from the file, then feed it to anistringstreamto split the fields. Clean and robust. - 💡 Let RAII help, but flush on purpose: the stream closes itself at scope end, but call
.close()when another reader needs the data now.
🎉 Lesson Complete
- ✅
ofstreamwrites,ifstreamreads,fstreamdoes both — all use<</>> - ✅ Always check
is_open(); loop on the read so EOF stops you cleanly - ✅
getlinegrabs whole lines;>>grabs tokens and converts types - ✅
ios::appappends,ios::binarywrites raw bytes - ✅
stringstreamparses and builds text in memory — same stream skills, no file needed - ✅ Close files (or rely on RAII) so the buffer is flushed
- ✅ Next lesson: Exception Safety — keep your resources sound when things go wrong
Sign up for free to track which lessons you've completed and get learning reminders.