!eof
From Cppwiki
Avoid using EOF as a loop invariant.
Contents |
Problem statement
while (!stream.eof()) { stream >> data; process(data); }
stream >> data typically stops upon whitespace or failure, leading to two problems.
- The final datum may be processed twice if it is followed by a newline. Input will stop at that newline before reaching EOF.
- The loop may never terminate if the stream fails for any reason other than EOF, such as malformed input (e.g. you attempted to read an integer but encountered an alphabetic character instead).
Prescribed solution
Make sure to check for failure after attempting any input operation.
Stop on failure
The simplest algorithm simply stops on any failure.
while (true) { stream >> data; // 1a. Try to read data. if (stream.fail()) break; // 1b. If input failed, stop. process(data); // 2. Process data, then go to step 1. }
Note that the expression stream >> data usually returns a reference to stream:
while (!(stream >> data).fail()) { // 1. Read data or stop. process(data); // 2. Process data, then go to step 1. }
Note also that, when used in a boolean context, stream evaluates to !(stream.fail()):
while (stream >> data) { // 1. Read data or stop. process(data); // 2. Process data, then go to step 1. }
Handle malformed input, stop on failure
If you encounter malformed input in the stream, you might tell it to advance to the next character in the stream and try to continue reading:
while (stream) { if (stream >> data) { process(data); // We read data; process it! } else { stream.clear(); // Something happened. Clear any errors stream.ignore(1); // and try to ignore the next character. } }
Handle any failure
There are three events to consider:
- stream.fail(): the stream failed to provide the requested data.
- stream.bad(): the stream lost integrity (i.e. something really "bad" happened) during the last operation. This occurs with typically indicates a problem with the stream's underlying buffer.
- stream.eof(): the stream reached EOF during the last operation.
Note that EOF does not itself indicate failure; you may have successfully read data but also reached EOF. "Failure upon EOF" generally means that previous operations had already read all the data.
Note also that there's another function, stream.good(), which is defined as !(stream.fail() || stream.eof() || stream.bad()) and effectively means "nothing happened during the last input operation (or I already took care of it)."
These may be useful if you need to distinguish between the various cases. For example, you could write a slightly more verbose error recovery routine:
while (stream.good()) { if (stream >> data) { process(data); } else if (stream.eof()) { break; // just ran out of stuff } else if (stream.bad()) { cerr << "something really bad happened\n"; break; } else { stream.clear(); cerr << "error at position " << stream.tellg(); if (stream.get(c)) { cerr << ": expecting int, got '" << c << "'\n"; } else { cerr << ": error reading int\n"; // ??? } } }
Fun summary example:
std::istringstream stream("12 34 56x 789"); int data; char c; assert(stream.good() && !(stream.eof() || stream.bad() || stream.fail())); stream >> data; assert(data == 12); assert(stream.good() && !(stream.eof() || stream.bad() || stream.fail())); stream >> data; assert(data == 34); assert(stream.good() && !(stream.eof() || stream.bad() || stream.fail())); stream >> data; assert(data == 56); assert(stream.good() && !(stream.eof() || stream.bad() || stream.fail())); stream >> data; assert(stream.fail()); assert(!(stream.good() || stream.eof() || stream.bad())); stream.clear(); assert(stream.good() && !(stream.eof() || stream.bad() || stream.fail())); stream >> c; assert(c == 'x'); assert(stream.good() && !(stream.eof() || stream.bad() || stream.fail())); stream >> data; assert(data == 789); assert(stream.eof()); assert(!(stream.good() || stream.bad() || stream.fail())); stream >> data; assert(stream.eof() && stream.fail()); assert(!(stream.good() || stream.bad()));
See also
- Marshall Cline's C++ FAQ Lite