while (!stream.eof()) is (usually) wrong
Thanks to John Hein for pointing out that stream.good() isn't necessarily the inverse of stream.fail(). :)
This topic has its own entry as FAQ 15.4 in Marshall Cline's C++ FAQ Lite, but I thought I could be just a wee bit clearer.
In C++, you can keep reading from an input stream until it fails. Several things can cause an input stream to fail, including EOF. You can determine whether input has failed, as well as the cause of the failure, by querying the following functions:
stream.eof() is true if an input operation attempted to read a character when no more are available.
stream.bad() is true if the stream has experienced a loss of integrity.
(This is fairly rare; I've only seen it happen when the underlying data stream code
throws an exception, and most do not do that.)
stream.fail() is true if the stream has failed to provide the requested input for any reason,
including unexpected input (e.g. encountering a letter in the stream while expecting a digit).
stream.good() is true if all of the above are false.
Generally speaking, input failure cannot be detected until after the failure has occurred. With that in mind, consider the following code:
while (!stream.eof()) { // 1. If input failed due to EOF, stop. stream >> data; // 2. Try to input data. process(data); // 3. Process data. } // 4. Repeat steps 1-3.
Step 1 will exit the loop only after Step 2 fails to input data due to EOF. However, the program processes data in Step 3 without checking whether Step 2 actually managed to input data. Further, Step 1 doesn't exit the loop if input fails for reasons other than EOF, e.g., if data is an integer and the user enters "Bob" (which is not an integer) instead.
Thus, the program should do something like this:
while (true) { stream >> data; // 1. Try to input data. if (stream.fail()) break; // 2. If input failed (for any reason), stop. process(data); // 3. Process data. } // 4. Repeat steps 1-3.
It's possible (and usually better) to do all of this in one expression.
Most IOstream operations, such as (stream >> data),
return the stream upon which they operate.
This allows a single expression to perform multiple operations in a single expression,
as in (stream >> x >> y >> z).
That means that you can do an input operation, then test whether it succeeded,
all in one expression:
while (!(stream >> data).fail()) { // 1. Input data or stop. process(data); // 2. Process data. } // 3. Repeat steps 1-2.
Moreover, the C++ stream classes can be used in a boolean context to determine whether or not the stream has failed:
while (stream >> data) { // 1. Input data or stop. process(data); // 2. Process data. } // 3. Repeat steps 1-2.