That's possible. C++ is new to me.
Then I expect this loop to end, and it does so as far as I tested.
Yes, but why? That's the heart of understanding C++ IO. It
ends because a call to getline fails, because there is no line
to get.
Basically, an istream maintains three "status" bits: badbit,
failbit and eofbit. In practice (regretfully), you can almost
forget about badbit, since most implementations will never set
it. (The intent is that it be set in cases of a "hard" error,
such as a read error on disk. In practice, most, if not all
implementations just treat this as if it were end of file.) The
most important status bit, in the first instance, is failbit; it
is set if the preceding read failed, either because there was no
data to read (end of file), or because the input had the wrong
format (impossible with getline, but if the input stream has
something like "abc" when you try to read an int). Finally,
eofbit is a bit tricky, because it represents internal status:
eofbit will be set anytime the implementation sees an end of
file, even when reading ahead. About the only time you check
eofbit is after a read failed: if the read failed and eofbit is
not set, then the input was incorrectly formatted; otherwise,
there's a good chance that you've reached the end of file.
Note that with getline on a string, there are only two possible
causes of error (beyond hardware read errors): you run out of
memory, or you reach end of file without seeing a new line, but
after having seen at least one character. In the first case,
badbit should be set (but it wouldn't surprise me if some
implementations let the bad_alloc exception leak out). In the
second, failbit gets set, but of course, end of file has been
seen internally, so eofbit is also set. Typically, this will
result in the last, incomplete line not being seen by the
program (and this is what will happen with your code if the
input file doesn't end with a newline character). If this is
not the behavior you want, then you need something like:
while ( getline( fileIn, line ) || fileIn.gcount() != 0 ) {
if ( !fileIn ) {
// incomplete last line...
}
// ...
}
Depending on context and what you are doing, this may or may not
be important.
I really didn't want to start a war in this thread.
You didn't. Obviously, opinions as to when to use exceptions
vary, but in fact, my response was very oriented to you, the
context of your posting, and you're apparent programming level
in C++; Ian probably summed it up best with his response to me:
you don't use exceptions for a missing file unless the fact that
it is missing is somehow exceptional. (Which of course begs the
point as to what is exceptional, but in a short program which
reads a more or less random file, the fact that the file isn't
there could hardly be considered exceptional.)
I thought it is a basic
question with an easy answer, but unfortunately there is none.
Nope. There are no easy answers
. C++ generally offers
several possible solutions to any problem, and the "best", or
even the "correct" solution, will depend on the context in which
the problem occurs.
But I have the feeling that I should drop the exceptions
approach for a missing input file.
For small programs which receive input data from a file,
definitely. For larger and more complex applications, it will
depend on the context: if the missing file is something that you
sort of expect from time to time (e.g. the filename was provided
by a user), exceptions probably aren't appropriate either; if
the missing file is part of your "system" (the deliverables
which should be installed together), or is a file that should
have been written by some other part of the application, an
exception is probably most appropriate, although rather than
playing around with the exception mask in the istream, I'd write
something like:
std::ifstream fileIn( name );
if ( !fileIn.open() )
throw ...
Obviously it's *not* bad practice just to check the ifstream in an if
statement. So I end up with this code:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main() {
ifstream fileIn( "file" );
if ( !fileIn ) {
cout << "Error.\n" << endl;
// Some people are confused that the program does not exit on
// missing input file, so here it is.
Any error should result in an error return status, if there's
the slightest chance your program might be used in a script, and
with an error message on cerr (rather than cout) if there's the
slightest chance your program might be invoked on the command
line.
The choice of -1 probably isn't optimal here. The only value
guaranteed to be treated as an error is EXIT_FAILURE; under Unix
and Windows, there is a tradition that errors are small positive
integers, and it is acceptable on these platforms to return
different small positive integers to distinguish between
different "errors". The Unix programs grep return 1 if they
don't find the string, for example, and 2 if they weren't able
to open the file.
(FWIW: under Unix, the return code will be coerced into an
unsigned char, thus -1 becomes 255. And the convention is that
errors from the program be in the range 1-127; error codes above
that are reserved for cases where the program has crashed.)
string line;
while ( getline( fileIn, line ) ) {
cout << line << endl;
}
fileIn.close();
return 0;
I hope this is correct and not bad programming practice,
correct me otherwise.
It's fine; my comments above are really nits (except maybe for
the case where the file doesn't end with a new line---if you're
on Windows, try entering "abc", and nothing else, in Notepad,
then save it and use it to test your program).
Another improvement that you'd want to make in production code:
before returning 0:
cout.flush();
if ( !cout )
// Write error in the output...
This is more important than closing the input (here, since the
input will be automatically closed when fileIn goes out of
scope). If for some reason (e.g. disk full) there has been
a write error, the output data will not be complete, and your
user should definitely be informed.