Programming Fundamentals Using C++

L29 · Text Files with `ifstream`/`ofstream`: Reading, Writing, Appending, EOF

Module 15 — File Handling and Error Management · Week 15 · Lecture 29 of 32 · 120 minutes Outcomes: CLO-8 · PF-15.1, PF-15.2 · LEARNING_OUTCOMES.md

Learning objectives

  1. Open text files for reading (std::ifstream) and writing/appending (std::ofstream with std::ios::app), always checking open success before use (PF-15.1).
  2. Read files in all three canonical ways — token (>>), full line (getline), and record (token + getline combination) — and write formatted records that round-trip (PF-15.1, PF-15.2).
  3. Implement the two correct EOF-loop idioms and explain why while (!in.eof()) with an in-body read is wrong (PF-15.2).

Prerequisites

L04 (stream state, clear/ignore); L21 (getline, the leftover-newline trap — now solved systematically); L28 (struct records — the data being persisted); M9 (arrays for loaded data).

Concept sequence

  1. Why files: persistence beyond main's lifetime (capstone requirement)
  2. <fstream>: ifstream/ofstream; open modes; open-check discipline
  3. Reading: token loops, line loops, record parsing
  4. Writing & appending: ofstream, std::ios::app; round-trip thinking
  5. EOF: the two correct idioms and the one classic bug
  6. Connecting to the capstone: load/save functions for records

Teaching topics (detailed)

C++ examples required

FileRole
file_read_write.cpp ✅token read, line read, append log, record save/load round-trip with open checks throughout
(live) roster_persist.cppL28's roster gains save_roster/load_roster — the capstone skeleton completes

Common student misconceptions

Conceptual explanation (beginner-first)

Variables live and die with the program. Close the terminal and the roster, the scores, the diary — all gone. Files make data outlive the program: same streams you already know (cin/cout are streams; a file is just another stream connected to disk), so the reading and writing skills transfer directly.

The workflow has four stations, each with a check: open (does the file exist / is it writable?), read or write (same >>, getline, and << you've used all semester), close (or let the stream object die at scope exit — its destructor closes, another RAII echo from L27), and verify (stream state tells you whether the last operation worked).

Two traps deserve their fame. Opening a file for output truncates it instantly — open for append (std::ios::app) if you mean to add. And the eof trap: testing while (!in.eof()) reads one line too far, because eof becomes true only after a read fails. The course idiom — reading as the loop condition (while (std::getline(in, line))) — sidesteps the whole trap: the read both attempts and reports.

Terminology and definitions

TermDefinition
ifstreamInput file stream — reads a file
ofstreamOutput file stream — writes (truncates by default!)
fstreamBoth directions; modes selected by flags
Open modesstd::ios::app (append), std::ios::trunc (default for output)
is_open() / stream-as-boolThe two accepted ways to check an open succeeded
Read-as-test idiomwhile (getline(in, line)) — attempt and check in one
eof trapeof() reflects the previous read; testing it first over-reads
Record round-tripSave structured records, load them back identically
Text format contractThe exact delimiter/order rule a loader can parse
RAII closeThe stream's destructor closes the file at scope exit

Syntax and C++ examples

#include <fstream>

// writing: output mode TRUNCATES an existing file
std::ofstream out{"scores.txt"};
if (!out)                          // check immediately, every time
{
    std::cerr << "cannot open scores.txt for writing\n";
    return 1;
}
out << 92 << ' ' << 87 << ' ' << 74 << '\n';
// out closes automatically at scope exit (RAII)

// appending: keeps existing content
std::ofstream logFile{"diary.txt", std::ios::app};
logFile << "new entry\n";

// reading token-by-token — read-as-test, no eof() in sight
std::ifstream in{"scores.txt"};
if (!in) { std::cerr << "missing scores.txt\n"; return 1; }
int score{};
while (in >> score)                // attempt+check in one condition
    std::cout << score << ' ';

// reading line-by-line
std::ifstream lines{"diary.txt"};
std::string line;
while (std::getline(lines, line))
    std::cout << line << '\n';

Line-by-line code explanation

examples/file_read_write.cpp (four sections, open checks throughout):

  1. Token write/read: writes three ints, closes, reopens, reads with while (in >> score) — the stream converts text back to numbers automatically; a non-numeric token fails the stream and ends the loop (the L04 input-state story, at file scale).
  2. Line read: getline loop preserves spaces — for record-ish text; demonstrates the while-condition idiom and prints a numbered report.
  3. Append log: opens twice — once in default (truncating) mode, once in append — and the file's before/after contents are printed: truncation shown, not asserted.
  4. Record round-trip: saves a mini-roster as name,score lines, then loads it back into a vector<Student>-style structure and prints it — the format contract (comma delimiter, one record per line, name has no commas) stated in a comment both functions reference.

Output prediction questions (with answers)

  1. Open diary.txt (5 entries) for output and write 1 line — how many entries remain? — 1: output mode truncated the file.
  2. while (!in.eof()) in >> x; with trailing newline — ? — The last value is processed twice: eof is checked before the failing read.
  3. Reading 92 87 abc 74 with while (in >> x) — which scores print? — 92, 87; the abc fails the stream, so 74 is never reached.
  4. Two runs of the append program — how many new entry lines? — 2: append preserves.
  5. What if the load function's contract expects , but the file used spaces? — Every record parses wrong (name absorbs the digits) — format contracts must match exactly.

Common errors and debugging examples

ErrorSymptomFix
No open checkReads fail silently — empty resultsif (!in) right after construction
Output-mode data lossAn existing file's contents vanishAppend mode (std::ios::app) for adding
eof() as the loop conditionLast record double-processedRead-as-test: while (getline(in, line))
Mixed >> and getline on filesgetline gets leftover newlinein.ignore(...) after token reads (L04 rule)
Format contract driftLoader mis-parses every recordComment the contract at both ends; round-trip test
Hardcoded absolute pathsWorks on one machine onlyRelative paths / project directory

Classroom demonstrations

  1. Truncation horror: create a 5-line diary, run the output-mode writer, type/cat the file — one line remains. Append mode rerun: six lines. Never forgotten afterwards.
  2. The eof double-read: run the eof-bug loop with a printed counter — the phantom extra iteration appears; replace with the read-as-test idiom and it vanishes.
  3. Round-trip ceremony: save the roster, show the raw text file, load it in a fresh run — data outlives the program, visibly.

Guided student activities

Round-trip sabotage (20 min): pairs receive a save function and a load function with a mismatched format contract (delimiter clash, missing ignore, wrong order); they must (1) break a supplied data file, (2) fix the contract, (3) prove the round-trip with before/after file dumps.

Practice problems

Summary

Files are streams to disk: open (and check!), read/write with the familiar operators, close by RAII. The two famous traps — truncating output mode and the eof double-read — both have one-line course idioms (std::ios::app; read-as-test). Record round-trips make data outlive the program and complete the capstone's persistence piece. Next (L30): the full input-validation and error-handling toolkit.

Exit ticket / formative assessment

  1. Write the open-and-check idiom for reading data.txt.
  2. Which idiom counts lines correctly — while (getline(...)) or while (!eof())? Why in one clause?
  3. What flag appends instead of truncates?

Estimated time allocation (120 min)

SegmentMinutes
Recall (structs quiz) + persistence motivation10
Open/check + modes + reading trio40
Break10
EOF idioms + record round-trip + capstone persistence35
Round-trip sabotage activity20
Exit ticket + L30 preview5
Programming Fundamentals Using C++ · C++17 · 16 weeksBack to top ↑