C++ / CONSOLE AND FILE INPUT-OUTPUT
File streams for reading text files
Open a text file with std::ifstream, verify it opened, read values with the stream-state loop, and tell a clean end of file apart from malformed data.
What you will learn
- Construct std::ifstream with a path and test if (!in) before the first read
- Drive reads with while (in >> x), never with while (!in.eof())
- After the loop use eof() then fail() to separate a finished file from bad data
- Read bytes verbatim with istreambuf_iterator when >> would swallow the whitespace
Understanding File streams for reading text files
std::ifstream is a std::istream whose characters come from a file instead of the terminal. Constructing one with a path opens the file for reading, the object owns the file handle, and its destructor closes it, so you seldom call close() by hand. Everything you already know about istream applies unchanged: >>, get(), ignore(), the state flags. The one genuinely new thing is that opening can fail, and C++ reports that by leaving the stream in a failed state rather than by throwing.
That is why a read loop tests the read itself. while (in >> value) performs the extraction and then converts the stream to bool, which is false the moment failbit or badbit is set, so the loop stops exactly when a value could not be produced. Extraction also stops at the first character it cannot use and leaves that character sitting in the buffer, so nothing is ever silently skipped. A loop written as while (!in.eof()) runs one iteration too many, because eofbit is set only after a read has already tried to fetch characters past the last one, and the body then reprocesses whatever the variable held before.
When the loop ends, failbit is set by definition, since that is what ended it, so the informative question is whether eofbit is set too. Both bits together mean the read stopped because there was nothing left, which is a normal finish; failbit alone means unparsable text sits somewhere before the end. Asking eof() first and fail() second converts that pair of bits into the two answers you actually care about, a distinction that barely matters for keyboard input but is the everyday case for files. badbit is a third outcome, reserved for real I/O trouble such as a vanished network mount.
<fstream>
<iostream>
int main() {
{ // create the input file so this example is self-contained
std::ofstream out("readings.txt");
out << "12.5 13.0 bad 14.25\n";
} // out's destructor closes the file
std::ifstream in("readings.txt");
if (!in) {
std::cerr << "could not open readings.txt\n";
return 1;
}
double value = 0.0;
double sum = 0.0;
int count = 0;
while (in >> value) {
sum += value;
++count;
}
if (in.eof())
std::cout << "clean end of file\n";
else if (in.fail())
std::cout << "stopped on unparsable text\n";
std::cout << "read " << count << " values, sum " << sum << '\n';
}
Reading a text file means driving the stream's state machine: the loop condition is the read itself, and the flags left behind tell you why it stopped.
Worked examples
A file that isn't there
Shows how a failed open is reported and what happens if you read anyway.
<fstream>
<iostream>
<string>
int main() {
std::ifstream in("does-not-exist.txt");
std::cout << std::boolalpha;
std::cout << "is_open: " << in.is_open() << '\n';
std::cout << "fail: " << in.fail() << '\n';
std::string word = "untouched";
in >> word;
std::cout << "word: " << word << '\n';
}
Example explained
Line 1The constructor attempts the open and, when it cannot, sets failbit; no exception is raised, so execution continues as if nothing happened.
Line 2is_open() answers "is a file attached?" while the boolean test used by if (!in) answers "is this stream usable?" — two different questions that happen to agree only here.
Line 3The relative path is resolved against the process's current working directory, not the directory holding the source file, which is the usual reason a path that looks right fails.
Line 4in >> word does nothing at all because the input sentry refuses to run on an already-failed stream, so word keeps its old value instead of being cleared.
Formatted extraction versus raw bytes
Contrasts reading a file with >> against copying its characters exactly as stored.
<cstddef>
<fstream>
<iostream>
<iterator>
<string>
int main() {
{
std::ofstream out("notes.txt");
out << "alpha beta\ngamma\n";
}
std::ifstream raw("notes.txt");
std::istreambuf_iterator<char> first(raw), last;
std::string text(first, last);
std::cout << "raw bytes: " << text.size() << '\n';
std::ifstream words("notes.txt");
std::string w;
std::size_t chars = 0;
int n = 0;
while (words >> w) {
chars += w.size();
++n;
}
std::cout << "tokens: " << n << ", token chars: " << chars << '\n';
}
Example explained
Line 1A default-constructed std::istreambuf_iterator is the end-of-stream marker, so first and last delimit every character the file buffer will hand out.
Line 2Building the string from that range copies bytes verbatim, keeping the space and both newlines, which is why the count is 17 rather than 14.
Line 3The second stream uses >>, which discards leading whitespace and stops at the next whitespace, so the three tokens together hold only the 14 non-space characters.
Line 4Two ifstream objects can have the same file open for reading at once, each with its own independent read position.
Important notes
is_open() and if (!in) are not interchangeable: after a normal read loop the file is still open, yet the stream tests false because failbit is set.
A truncated last token, such as a file ending in 1.2e, sets eofbit and failbit together, so the eof-first test calls it a clean end; if corrupt tails matter, also verify that you read as many values as you expected.
Common mistakes
Writing while (!in.eof()) { in >> x; use(x); }: eofbit appears only after a read has overrun the end, so the body runs one extra time and the last value is used twice.
Skipping the if (!in) check: a wrong or wrongly-relative path produces zero loop iterations, and the program reports an empty file when the real problem is a missing one.
Writing a Windows path as "C:\temp\notes.txt" with single backslashes, where \t and \n become a tab and a newline, so the open fails for a reason the program never mentions.
Try it yourself
Change, predict, then run
Write a program that creates scores.txt containing 90 85 seven 70, reads ints with while (in >> n), and prints the count together with whether the loop ended at end of file or on bad data. Then replace seven with 75 and confirm the message and count both change.
Open the C++ workspaceCheck your understanding
A file holds the text 10 20 abc 30, and the code is: int n; int count = 0; while (in >> n) ++count;. What is true right after the loop?
- count == 3 and eof() is true, because >> skips the token it cannot parse and carries on
- count == 2 and eof() is true, because every failed extraction also sets eofbit
- count == 2 and eof() is false, because extraction stopped at 'a' and left it unread
- count == 2 and fail() is false, because abc was converted to 0 and the read succeeded
Show answer
The third extraction finds 'a', which cannot start a number, so it consumes nothing, sets failbit, and leaves the character in the buffer; no read ever went past the end of the file, so eofbit stays clear. Option 1 tempts because a well-formed file does end with failbit and eofbit both set, but that is exactly the case eof() is there to distinguish: it means the loop stopped for lack of input, not for lack of a valid number.