Recipe
Read a text file to the end
Use when: a program has to take in every line, or every value, of a text file, and you don't know in advance how long the file is
Goal: every line or value processed once, plus an answer to why reading stopped: the file ran out, or it held something that couldn't be read
Taught in Reading a file to the end
Quick recipe
-
1
Create an input file stream with the file's name. A name with no folder in it, such as
scores.txt, is looked for in the program's working directory (§1).Example
#include <fstream> std::ifstream in{"scores.txt"};Why: the stream is what every later read comes from, and it reads just like
std::cin. -
2
Straight after opening, test the stream with
if (!in)and say so if the file couldn't be opened.Example
#include <fstream> #include <iostream> int main() { std::ifstream in{"data.txt"}; if (!in) { std::cout << "can't open data.txt\n"; return 1; } }can't open data.txtRun in a folder with no
data.txt, it says so. If you see this message, the file isn't in the working directory, or the name is misspelled.Why: a failed open is silent. Without the check, a missing file and an empty file look the same: a loop that runs zero times.
-
3
Look at how the file is laid out, and choose the read to match.
- each line is one record, and you want it whole, spaces included — read with
std::getline(in, line)into astd::string. - the file is values separated by spaces or newlines, such as
Ada 91— read with>>, and chain one>>per field:in >> name >> score.
Why:
getlinekeeps everything up to the newline.>>skips whitespace and turns the characters into the variable's type, one value at a time. - each line is one record, and you want it whole, spaces included — read with
-
4
Write the read inside the
while (...), never a separateeof()test before it. The body then only runs after a read that worked.Example
The file holds
10,20and30, one per line.while (!in.eof()) { in >> x; sum += x; }givessum 90after 4 rounds. The fourth read fails and 30 is added twice.while (in >> x) { sum += x; }givessum 60after 3 rounds.
Why: a read returns the stream, and the stream counts as
falseonce a read has failed. So the test happens after the read it depends on.eof()only turns true after a read has already tried to go past the end, one round too late. -
5
Do the work for each line or value in the body: count it, add it, compare it. Copy anything you'll want after the loop into a variable of its own, while the value is still there.
Example
To find the longest line, compare inside the loop and keep a copy:
if (line.size() > best.size()) { best = line; }. After the loop, printbest, notline.Why: the loop ends because a read failed. When the file ends with a newline, that failed
getlineleaveslineempty. -
6
Test
in.eof()once the loop has ended.Example
12 15\n14 oops 16\nread withwhile (in >> value)stops after 3 values, witheof()false:stopped at bad data after 3 values. With the file fixed to12 15\n14 16\n,eof()is true:read all 4 values.in.eof()is true — the whole file was read. That's the normal ending.in.eof()is false — a read failed before the end, on characters that didn't fit the type, such as the wordoopswhere anintbelonged. Report it, with how far you got.
⚡ Shortcut
A
getlineloop almost always stops because the file ran out, since any characters at all fit into astd::string. The bad-data question matters most for>>loops, where the text has to become a number.Why: both endings look the same from inside the loop, because the read simply returned
false. Only the stream's flags remember which one it was.
Common mistakes
- Writing
while (!in.eof())and then reading inside the body. The last value is used twice, or one empty line too many appears. - The same
while (!in.eof())loop with>>, on bad data or a file that never opened. The failed read never moves on,eof()never turns true, and the loop never ends. - Skipping the open check, then puzzling over a loop that never runs.
- Using
lineorxafter the loop and expecting the last good value. - Treating the end of the loop as success, so a file with bad data in the middle gives a total that stops partway with no warning.