ifstream errors

A

adramolek

So... I'm trying to get used to using C++ ifstream (or ofstream)
instead of stdio (although I'm having second thoughts). Anyways, I
want to be able to display a meaningful error message if ifstream
fails to open a file, but nothing I read about ifstream says anything
about a reliable place to get an error message. Using stdio I can
simply do:

FILE *f = fopen(filename, "rb");
if (!f)
perror(filename);

So far the best I've been able to do using ifstream is:

ifstream f(filename, ios_base::in | ios_base::binary);
if (!f.is_open())
cerr << filename << ": tough luck!" << endl;

What is a reliable way to get a real error message? Even the example
at cplusplus.com:

http://www.cplusplus.com/reference/iostream/ifstream/is_open.html

Gives a crappy error message.

Thanks,
AJ
 
A

adramolek

What is a reliable way to get a real error message?

Oh, also, related to this, how do I tell if >> fails? Say I am reading
4 integers from an ifstream opened in text mode. Using stdio:

int a, b, c, d;
if (fscanf(infile, "%i%i%i%i", &a, &b, &c, &d) != 4)
fprintf(stderr, "error parsing line\n");

But using ifstream:

int a, b, c, d;
infile << a << b << c << d;
// how to check for failure...?


Thanks
 
A

Abhishek Padmanabh

So... I'm trying to get used to using C++ ifstream (or ofstream)
instead of stdio (although I'm having second thoughts). Anyways, I
want to be able to display a meaningful error message if ifstream
fails to open a file, but nothing I read about ifstream says anything
about a reliable place to get an error message. Using stdio I can
simply do:

FILE *f = fopen(filename, "rb");
if (!f)
  perror(filename);

So far the best I've been able to do using ifstream is:

ifstream f(filename, ios_base::in | ios_base::binary);
if (!f.is_open())
  cerr << filename << ": tough luck!" << endl;

What is a reliable way to get a real error message? Even the example
at cplusplus.com:

http://www.cplusplus.com/reference/iostream/ifstream/is_open.html

The error message has to be your own. Or, you could set up
ios::exceptions (for the failure bits you want to associate exceptions
with) with your fstream objects. The site above shows samples of how
to do that.

Using member operator! to test file open failure might make it look
easier. For example:

ifstream f(filename, ios_base::in | ios_base::binary);
if (!f) //no need to call is_open
cerr << filename << ": tough luck opening file!" << endl;

Failures with fstream objects are notified by setting the following
bits: eofbit, failbit and badbit. So, if you want granularity in
reporting errors, you would need to check them individually. You would
need to go through the documentation of the members of fstream objects
to see which operations' failure set which bits.
 
M

Michael.Boehnisch

On 27 Mrz., 09:33, (e-mail address removed) wrote:
[..]
But using ifstream:

int a, b, c, d;
infile << a << b << c << d;
// how to check for failure...?

if ( infile.fail() ) {
....
}

best,

Michael
 
J

James Kanze

So... I'm trying to get used to using C++ ifstream (or
ofstream) instead of stdio (although I'm having second
thoughts). Anyways, I want to be able to display a meaningful
error message if ifstream fails to open a file, but nothing I
read about ifstream says anything about a reliable place to
get an error message. Using stdio I can simply do:
FILE *f = fopen(filename, "rb");
if (!f)
perror(filename);

Whether that gives you something meaningfull or not depends
somewhat on the implementation. What perror outputs depends on
errno, and the standard really doesn't specify too much in this
regard.
So far the best I've been able to do using ifstream is:
ifstream f(filename, ios_base::in | ios_base::binary);
if (!f.is_open())
cerr << filename << ": tough luck!" << endl;

Why not?

ifstream f(filename, ios_base::in | ios_base::binary);
if (!f.is_open())
perror( filename ) ;

You're very much in the domain of implementation defined, or
even unspecified, both with FILE* and ifstream, but in general,
I'd expect both of them to behave more or less identically here.
(Under Unix, I'm pretty sure they will; errno is set by the
functions in the system API which ifstream or fopen must call.
They seem to behave the same under Windows as well, at least
with VC++.)
 
J

James Kanze

On Mar 27, 9:33 am, (e-mail address removed) wrote:

[...]
int a, b, c, d;
if (fscanf(infile, "%i%i%i%i", &a, &b, &c, &d) != 4)
fprintf(stderr, "error parsing line\n");
But using ifstream:
int a, b, c, d;
infile << a << b << c << d;
// how to check for failure...?

if ( ! infile ) ...

You can use an istream as a boolean; it will behave as true if
no error has occured, and as false once an error has been seen.
Once you've detected an error (and only then), you can use more
specific functions to determine the cause:

if ( ! infile ) {
if ( infile.bad() ) {
// Serious hardware problem... (read error, etc.)
// A lot of implementations aren't too rigorous
// about reporting this, and you might never see
// it.
} else if ( ! infile.eof() ) {
// Format error in the input stream.
} else {
// Probably an end of file (although in a few rare
// cases, infile.eof() can return true even though
// there was a format error in the input).
}
}

The error condition is sticky: it must be cleared (function
istream::clear()) before you can read further (or do anything
else) with the stream.

A typical idiom for reading files is:

while ( infile >> a >> b >> c ) {
// ...
}

or (more often, because it allows better recovery in case of a
format error):

std::string line ;
while ( std::getline( infile, line ) ) {
std::istringstream parse( line ) ;
parse >> a >> b >> c >> d >> std::ws ;
if ( ! parse || parse.peek() != EOF ) {
// Syntax error in the line...
} else {
// ...
}
}

This has the advantage of not putting the main input in an error
state, and leaving it correctly positionned for further reading
in case of an error. (Like fscanf, the >> operators treat a new
line as white space. So if the input syntax is line oriented,
you probably want to use getline to read it, and istringstream
to parse each line. Something like you'd use fgets to read and
sscanf to parse in C, except that it doesn't require any special
handling for excessively long lines.)
 
J

Jim Langston

Oh, also, related to this, how do I tell if >> fails? Say I am reading
4 integers from an ifstream opened in text mode. Using stdio:

int a, b, c, d;
if (fscanf(infile, "%i%i%i%i", &a, &b, &c, &d) != 4)
fprintf(stderr, "error parsing line\n");

But using ifstream:

int a, b, c, d;
infile << a << b << c << d;
// how to check for failure...?


Thanks

if ( infile >> a >> b >> c >> d )
// success
else
// failure

Alternately

if ( ! ( infile >> a >> b >> c >> d ) )
// failure
else
// success
 
R

Ron Natalie

FILE *f = fopen(filename, "rb");
if (!f)
perror(filename);

If your machine has perror, it probably has strerror
which returns a char* with the same content.
 
A

adramolek

Thanks for your reply.

Whether that gives you something meaningfull or not depends
somewhat on the implementation. What perror outputs depends on
errno, and the standard really doesn't specify too much in this
regard.

But, on my system at least, fopen() is documented as setting errno to
EINVAL or the various errors caused by malloc() or open(). On the
other hand, ifstream is not documented as setting any kind of error
flags at all aside from the 3 failure bits that the other people here
mentioned. What am I missing?
Why not?

ifstream f(filename, ios_base::in | ios_base::binary);
if (!f.is_open())
perror( filename ) ;

Because nothing I've read about ifstream mentions errno. Is this
valid? For example, with fopen() I know that if it returns NULL errno
will be set to something meaningful... at least according to the man
page and past experience. However, what guarantees that the ifstream
constructor won't do, say, "errno = rand();" before it returns after a
failure (what I mean is, does it say somewhere that errno can be
relied on after an ifstream fail)?
You're very much in the domain of implementation defined, or
even unspecified, both with FILE* and ifstream, but in general,
I'd expect both of them to behave more or less identically here.
(Under Unix, I'm pretty sure they will; errno is set by the
functions in the system API which ifstream or fopen must call.
They seem to behave the same under Windows as well, at least
with VC++.)

Thanks,

AJ
 
A

adramolek

If your machine has perror, it probably has strerror
which returns a char* with the same content.

Thanks for your reply. Mostly what I was wondering about was if
ifstream sets errno.
 
A

adramolek

Using member operator! to test file open failure might make it look
easier.

Hey cool, thanks for the tip.
Failures with fstream objects are notified by setting the following
bits: eofbit, failbit and badbit. So, if you want granularity in
reporting errors, you would need to check them individually.

I see; this makes sense. It should be enough... mostly I'd like to be
able to notify the user about things like "file not found" as opposed
to "permission denied", though. Well I guess really those are the two
big errors. In the end it's not critical, it's just something I was
wondering about.

Thanks again, that helps a lot
AJ
 
A

adramolek

[a great reply]

Thanks a lot! That clears a lot of stuff up. The istringstream is
pretty cool, I was wondering if there was something like that.

I have one small new question left: Do istreams have anything like
scanf's %[] for matching only characters in a set (I think of it like
the poor man's regular expression)? It's something I've found pretty
handy sometimes in the past.

Thanks again,
AJ
 
J

James Kanze

On Mar 27, 6:24 am, James Kanze <[email protected]> wrote:
I have one small new question left: Do istreams have anything
like scanf's %[] for matching only characters in a set (I
think of it like the poor man's regular expression)? It's
something I've found pretty handy sometimes in the past.

No, but unlike scanf, it's totally extensible. I don't think
I've ever written an application where we didn't have some user
defined extractors or manipulators. Often, too, I'll use
something more or less like a decorator (it is a decorator,
vis-a-vis iostream), to control input or output in some special
way. Just write an extractor which operates indirectly on your
string.

Alternatively, you don't necessarily need a poor man's
replacement. Boost regular expressions works very well, and is
in the process of being adopted into the standard.
 
J

James Kanze

But, on my system at least, fopen() is documented as setting errno to
EINVAL or the various errors caused by malloc() or open(). On the
other hand, ifstream is not documented as setting any kind of error
flags at all aside from the 3 failure bits that the other people here
mentioned. What am I missing?

That's because your system documents the C API, and doesn't
document the C++ one. A function like fopen() is part of the C
standard, but also part of the Posix standard, and bundled in
the libc which is bundled with the OS.

In practice, all that they're doing is documenting the errno
values generated by the lower level functions. fopen() doesn't
actually set errno itself, ever. And of course ifstream::eek:pen()
will eventually call the same low level functions: if you're on
a Posix based system, the only way you're going to open a file
is with open(), and open() sets errno in case of an error. The
only thing that's really going to be different between
ifstream::eek:pen() and fopen() is the documentation.
Because nothing I've read about ifstream mentions errno. Is
this valid?

It will definitely work with Unix, unless the implementation is
very perverse: Posix also uses errno to report errors from it's
low level routines. I did a quick check with VC++ under
Windows, and it also worked. (Not too surprising, really. The
author of the VC++ standard library played a major role in the
standardization of the C library, many years back.)
For example, with fopen() I know that if it returns NULL errno
will be set to something meaningful... at least according to
the man page and past experience. However, what guarantees
that the ifstream constructor won't do, say, "errno = rand();"
before it returns after a failure (what I mean is, does it say
somewhere that errno can be relied on after an ifstream fail)?

Common sense? Quality of implementation? I agree that it
*should* be documented. But in practice, istream::eek:pen will
behave exactly like fopen.

One thing you might want to pay attention to: something like:

std::cerr << "the error was: " << errno << std::endl ;

may invoke an operator<< before reading errno (which can't
happen with the corresponding fprintf). Once you've detected an
error, unless you're using a library routine designed to exploit
errno, I'd save it in a local variable before anything else.
Other than that, it should be business as usual.
 
Joined
Jun 25, 2011
Messages
1
Reaction score
0
Hello,

with a lot of interest I followed this and other threads to get information about perror() in relation to working with streams with the aim to provide meaningful error messages while reading from files. As I found the available information not being insightful enough, I implemented some tests and wrote the following article:

http://gehrcke.de/2011/06/reading-f...rrectly-with-badbit-failbit-eofbit-and-perror

Some of the conclusions:

important things to know
===================
a) After std::getline(): "Notice that some eofbit cases will also set failbit." reference: http://www.cplusplus.com/reference/string/getline/
b) perror() evaluates the current setting of errno, a global error variable which is set by functions in the API of the current operating system. This errno setting is sticky: it stays until the next error is happening, overwriting the state of the last error. Therefore, perror() must only be called in a context that for sure has updated errno right before.

test results (important things to know 2)
==============================
All this makes makes a lot of sense:
1) The ifstream s ("file") constructor sets errno in case of a non existing file.
2) is_open() does not set errno.
3) is_open() does not catch the case when trying to open a directory.
4) is_open() only catches the non-existing-file-case.
-> is_open() after ifstream constructor in combination with perror() works, but only for catching this one case: non existing file.

5) getline() does only change errno in case of trying to read from a directory. In all other test cases it does not change errno.
6) In any case, reaching EOF has set the eofbit _and_ the failbit (which I do not understand, but which may happen as stated above)
7) the badbit was only set for the try-to-read-directory case
-> !s or s.fail() (equivalent, both are sensitive to badbit **and failbit**), must not be used in combination with perror(), because it may print error messages which are wrong in the current context.
-> Only a badbit check and therefore s.bad() seems to be safe to use together with perror().

Test details and working code samples which I think are pretty stable can be found in the article. I would be happy to get comments, especially if I got something wrong.

Cheers,

Jan-Philip
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top