Home > Net >  how to detect read/write errors when using fread() and fwrite?
how to detect read/write errors when using fread() and fwrite?

Time:08-06

In c i am trying to read/write to a file with fread/fwrite().

According to this answer i can use ferror() and feof() to check for errors.

My question is, how can i use them to do this?

CodePudding user response:

The basic recipe is

size_t r = fread(buf, m, n, fp);
if(r > 0)
    success;
else if(ferror(fp))
    handle error condition;
else
    handle EOF condition;

Basically, in this case, a 0 return from fread means "either EOF or error", and you have to do something else to figure out which. The "something else" is that feof(fp) is true if there was an EOF, and ferror(fp) is true if there was an error. (Whether it's possible to have both an EOF and error condition at the same time is an interesting question which I am not going to try to answer.)

But, it's true, having both feof() and ferror() available is, mildly, overkill. Typically you only need one or the other. In the code fragment above I used ferror but not feof. Obviously I could have arranged it a different way:

if(r > 0)
    success;
else if(feof(fp))
    handle EOF condition;
else
    handle error condition;

The other thing to remember, as @chux dicusses in his answer (and I got confused on in a previous version of this answer) is that fread can return less than you asked for. So it's not an error for fread to return a value less than n, but greater than 0 — and this indicates that you've probably hit end-of-file (and feof(fp) would be true).

Things are a little different for fwrite, which returns n on success or 0 on total failure. Theoretically, fwrite can return a number greater than 0 but less than n, and this would indicate, I guess, that it successfully wrote some number of items before it hit an error, but I don't know if that case ever actually happens in practice.

CodePudding user response:

@Steve Summit well covers the common cases with code like:

if (r > 0)
    success;
else if (feof(fp))
    handle EOF condition;
else
    handle error condition;

But we could dig deeper into:

size_t fread(void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream);
size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream);
  1. fread()/fwrite() returns 0 in 4 cases:
    1.1. An end-of-file just occurred. (fread() only)
    1.2. An end-of-file had occurred prior. (fread() only)
    1.3. A read/write error just occurred (e.g. reading a file opened in write only mode or visa-versa, a read parity error, etc.)
    1.4. size or nmemb is zero.

  2. Prior to the fread()/fwrite() call, the stream, which has 2 internal flags: end-of-file and error indicators, zero, either one or both of these may be set and are reflected in feof() and ferror(). Commonly these are both clear before the fread()/fwrite() call, but not always.

  3. Sometimes the amount of data desired to be read/written is less than requested.

Putting this together for fread()/fwrite()

size_t count = fread(ptr, size, nmemb, stream); // or fwrite
if (count == 0) {
  if (feof(stream)) {
    puts("End-of-file.");
  } else if (ferror(stream)) {
    puts("Stream error.");
  } else {
    puts("Request size 0.");  // Case not expected if size, nmemb are both > 0.
  }
} else if (count < nmemb) {
  puts("Less than expected data read/written.");
} else {
  puts("Success.");
}

There is value in checking feof() before ferror() as when the error indicator is set and fread() returns 0 due to end-of-file, the correct failure reason is reported. Note that fread()/fwrite() do not return 0 if the error indicator is set. They will return 0 if an error is just then detected and then set the error indicator.

  • Related