JDQ
JDQ

Reputation: 462

How to tell if opening input file failed in C

Motivation

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.

Question

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

Answers (2)

Kaz
Kaz

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 set errno, the standard C error-cause-reporting mechanism, unless for some reason it were difficult, or couldn't be done at all? If fopen can determine a specific cause for why the file cannot be opened, it's as simple matter to set errno 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

Peter
Peter

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

Related Questions