Is it possible to distinguish the error returned by fgets
Asked Answered
V

2

13

Upon looking at the ISO C11 standard for fgets §7.21.7.2, 3, the return value is stated regarding the synopsis code:

#include <stdio.h>
char *fgets(char* restrict s, int n, FILE* restrict stream);

The fgets function returns s if successful. If end-of-file is encountered and no characters have been read into the array, the contents of the array remain unchanged and a null pointer is returned. If a read error occurs during the operation, the array contents are indeterminate and a null pointer is returned.

The standard says that a null pointer is returned for either an end-of-file and no characters have been read in or a read error occurs. My question is, just from fgets, and the returned null pointer, is there a way to distinguish which of the two cases caused the error?

Valedictory answered 23/7, 2017 at 4:33 Comment(7)
I would suspect there is a reason for ferror().Mungovan
The functions feof() and ferror() are designed for that. Be aware that while (!feof(file)) is always wrong.Nickens
Beware, before you answer... "JUST from fgets, and the returned null pointer"Mencius
Anyway if you set to all 0 the input buffer you can see also what have partially been read.Kist
@Frankie_C-- "If a read error occurs during the operation, the array contents are indeterminate".Correct
@VidorVistrom that line could be a little more lax. The current conversion on ferror() and feof() is acceptable.Valedictory
Tell a programmer to buy coffee while he is out, he wont ever return!Mencius
F
9

Is there a way to distinguish which of the two cases caused the error?

Yes, use feof() and ferror() to distinguish. @Nothing Nothing


Yet it is important to use correctly. Consider the two codes:

char buf[100];
fgets(s, sizeof s, stream);
if (feof(stream)) return "End-of-file occurred";
if (ferror(stream)) return "Input error occurred"; 


if (fgets(s, sizeof s, stream) == NULL) {
  if (feof(stream)) return "End-of-file occurred";
  if (ferror(stream)) return "Input error occurred"; 
  return "Should never get here";
}

The second properly tests the return value against NULL, as suggested by OP.

The first can encounter a rare problem. The ferror(stream) tests a flag. This flag may have been set by a prior I/O function call on stream so this fgets() is not necessarily the cause of the error. Best to check the result of fgets() to see if this function failed.

If code is to continue using stream after an error detected, be sure to clear the error before continuing - like maybe to attempt a re-try.

if (ferror(stream)) {   
  clearerr(stream);
  return "Input error occurred");
}

Note that clearerr() clears both the error and end-of-file flags.

The same applies for feof(), yet most code is written to quit using stream once an end-of-file is true.


There is a 3rd pathological way to receive NULL and neither feof() nor ferror() returns NULL as detailed in Is fgets() returning NULL with a short buffer compliant?. Careful reading of the C spec has 3 "ifs", of which it is possible that not of them are true as so the spec is lacking - which implies UB.

Factious answered 23/7, 2017 at 11:16 Comment(1)
Maybe it's not a good practice to return strings, because program may contain i18n elements, so prefer to define a enum with error codes and return enum values.Ne
G
9

If the failure has been caused by end-of-file condition, additionally sets the eof indicator (see feof()) on stream. The contents of the array pointed to by str are not altered in this case. If the failure has been caused by some other error, sets the error indicator (see ferror()) on stream. The contents of the array pointed to by str are indeterminate (it may not even be null-terminated).

Therefore, you would need to check for feof() and ferror() in order to determine the error.

From this site

Graphitize answered 23/7, 2017 at 4:47 Comment(0)
F
9

Is there a way to distinguish which of the two cases caused the error?

Yes, use feof() and ferror() to distinguish. @Nothing Nothing


Yet it is important to use correctly. Consider the two codes:

char buf[100];
fgets(s, sizeof s, stream);
if (feof(stream)) return "End-of-file occurred";
if (ferror(stream)) return "Input error occurred"; 


if (fgets(s, sizeof s, stream) == NULL) {
  if (feof(stream)) return "End-of-file occurred";
  if (ferror(stream)) return "Input error occurred"; 
  return "Should never get here";
}

The second properly tests the return value against NULL, as suggested by OP.

The first can encounter a rare problem. The ferror(stream) tests a flag. This flag may have been set by a prior I/O function call on stream so this fgets() is not necessarily the cause of the error. Best to check the result of fgets() to see if this function failed.

If code is to continue using stream after an error detected, be sure to clear the error before continuing - like maybe to attempt a re-try.

if (ferror(stream)) {   
  clearerr(stream);
  return "Input error occurred");
}

Note that clearerr() clears both the error and end-of-file flags.

The same applies for feof(), yet most code is written to quit using stream once an end-of-file is true.


There is a 3rd pathological way to receive NULL and neither feof() nor ferror() returns NULL as detailed in Is fgets() returning NULL with a short buffer compliant?. Careful reading of the C spec has 3 "ifs", of which it is possible that not of them are true as so the spec is lacking - which implies UB.

Factious answered 23/7, 2017 at 11:16 Comment(1)
Maybe it's not a good practice to return strings, because program may contain i18n elements, so prefer to define a enum with error codes and return enum values.Ne

© 2022 - 2024 — McMap. All rights reserved.