Reputation: 462
If I am sure that a given input file exists, I would like to loop until creation of a "file stream" for the file does not fail.
I understand that with the following check we can tell if opening an input failed (and therefore, said file exists locally) in C,
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *rec_in_file = fopen("filename.txt", "r");
// If file cannot be opened, report error and exit
if (rec_in_file == NULL)
{
fprintf(stderr, "Could not open input file!");
exit(1);
}
return 0;
}
but how can I tell if the file exists but something went wrong while creating the "file stream"? In preliminary research for the question, I could file no such mention of handling this case.
Upvotes: 0
Views: 4291
Reputation: 58500
The fopen
function may, but might not, set errno
to indicate the specific cause of failure. If it doesn't, then the cause is likely not detectable, or you're dealing with a very poor implementation of the standard library.
Why wouldn't the implementor of
fopen
not seterrno
, the standard C error-cause-reporting mechanism, unless for some reason it were difficult, or couldn't be done at all? Iffopen
can determine a specific cause for why the file cannot be opened, it's as simple matter to seterrno
to some value. If this is so hard that the C library implementor ducks out of it, it's probably not going to be easy for you, either, on that platform.
Because fopen
might not set errno
(ISO C doesn't require it), this creates an ambiguity: if fopen
fails, and errno
is nonzero, we don't know whether fopen
set that value, or whether it was already nonzero. The solution is: assign zero to errno before trying fopen.
Tip: if you're working in an older dialect of C, or in a style which avoids mixed declarations and statements, you can still do this with a comma expression:
FILE *f = (errno = 0, fopen(...));
the value and type of the comma expression is that of the right operand.
Then, in the case that fopen
fails, test whether errno
has changed to nonzero. If so, it probably contains information pertaining to the fopen
. You can make an error message more informative, or take some special actions:
if (fp == NULL) {
if (errno != 0)
fprintf(stderr, "Could not open input file, for this reason: %s\n!",
strerror(errno));
else
fprintf(stderr, "Could not open input file, for some reason.\n");
}
Tip: you can use #ifdef
to see whether specific errno
constants exist. For instance if you want to check for and do something special for the POSIX EPERM
, wrap that code with #ifdef EPERM ... #endif
.
Note that the above errno
approach works fine on Microsoft Windows using the MSVCRT (Microsoft Visual C Run Time) C library (no Unix emulation layer). Microsoft's fopen
nicely sets errno
to 2, and the strerror
string for that is "No such file or directory"
. This 2 corresponds to the ENOENT
constant, which Microsoft provides. EPERM
is there and has value 1. The behavior of setting errno
is documented; the following quote is straight out of MSDN: "[i]f an error occurs, the global variable errno is set and may be used to obtain specific error information."
Microsoft's fopen
will also fail and set errno
to EINVAL
if you pass it null strings, and the invalid parameter handler is configured to allow execution to proceed.
Upvotes: 3
Reputation: 36597
Checking errno
or a call of perror()
can be used to get information after the fopen()
fails. The problem with that is that these means are not required to produce any information related to I/O errors. Implementations often do, but they are not required to, and the information varies.
Generally speaking, you will need to use functions outside standard C, such as access()
or stat()
(supported by most Unix libraries), PathFileExists()
(Windows API), or some other means provided by the host system.
Upvotes: 3