16

I have just recently started working with I/O in C. Here is my question -
I have a file, from which I read my input. Then I use fgets() to get strings in a buffer which I utilise in some way. Now, what happens if the input is too short for the buffer i.e. if the first read by fgets() reaches EOF. Should fgets() return NULL(as I have read in fgets() documentation)? It seems that it doesn't and I get my input properly. Besides even my feof(input) does not say that we have reached EOF.
Here is my code snippet.

char    buf[BUFSIZ];
FILE    *input,
        *output;

input   = fopen(argv[--argc], "r");
output  = fopen(argv[--argc], "w");

/**
 *  If either of the input or output were unable to be opened
 *          we exit
 */
if (input == NULL) {
    fprintf(stdout, "Failed to open file - %s.\n", argv[argc + 1]);
    exit(EXIT_FAILURE);
}

if (output == NULL) {
    fprintf(stdout, "Failed to open file - %s.\n", argv[argc + 0]);
    exit(EXIT_FAILURE);
}

if (fgets(buf, sizeof(buf), input) != NULL) {
    ....
}

/**
 *  After the fgets() condition exits it is because, either -
 *      1) The EOF was reached.
 *      2) There is a read error.
 */
if (feof(input)) {
    fprintf(stdout, "Reached EOF.\n");
}
else if (ferror(input)) {
    fprintf(stdout, "Error while reading the file.\n");
}
yadav_vi
  • 1,289
  • 4
  • 16
  • 46
  • 5
    Note that `feof(input)` will only return true *after* attempting to *read past* EOF. It doesn't tell you that you are *at* EOF. – lurker Feb 10 '14 at 13:54
  • Can you explain that in the context of the example above. Suppose, `fgets()` reads only `100 bytes` (`BUFSIZE` is 512), should `fgets()` return `NULL`? – yadav_vi Feb 10 '14 at 13:58
  • Ok, now I realise how dumb my comment was; anyways I get what you mean. How do I check if EOF was reached? – yadav_vi Feb 10 '14 at 14:04

1 Answers1

17

The documentation for fgets() does not say what you think it does:

From my manpage

fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.

And later

gets() and fgets() return s on success, and NULL on error or when end of file occurs while no characters have been read.

I don't read that as saying an EOF will be treated as an error condition and return NULL. Indeed it says a NULL would only occur where EOF occurs when no characters have been read.

The POSIX standard (which defers to the less accessible C standard) is here: http://pubs.opengroup.org/onlinepubs/009695399/functions/fgets.html and states:

Upon successful completion, fgets() shall return s. If the stream is at end-of-file, the end-of-file indicator for the stream shall be set and fgets() shall return a null pointer. If a read error occurs, the error indicator for the stream shall be set, fgets() shall return a null pointer, and shall set errno to indicate the error.

This clearly indicates it's only going to return a NULL if it's actually at EOF when called, i.e. if any bytes are read, it won't return NULL.

abligh
  • 24,573
  • 4
  • 47
  • 84
  • Can you explain `feof()` part, why doesn't it print anything. `EOF` has been reached right? – yadav_vi Feb 10 '14 at 14:00
  • `feof()` tests the end of file indicator, which is only set if you've already had an error where you have attempted to read past the end of the file. It does not (confusingly) indicate you are at the end of the file. – abligh Feb 10 '14 at 14:03
  • @yadav_vishal I had already explained `feof` behavior in my prior comment to your original post. – lurker Feb 10 '14 at 14:04
  • @mbratch Yeah, I read it again **carefully** and understood what you meant, but how do I check if I reached `EOF`. If not by `feof()` then how? – yadav_vi Feb 10 '14 at 14:07
  • 2
    You do the `fgets()` again, you will get `EOF`. That's the normal way to test. – abligh Feb 10 '14 at 14:09
  • 1
    @yadav_vishal what abligh said. You continue using `fgets` until you see it return `NULL` to end the loop. At that point, `feof` should be TRUE since the `NULL` means `fgets` attempted to read past EOF. – lurker Feb 10 '14 at 14:24
  • 1
    Got it! Thanks! I used an `if()`, should've used a `while()`. – yadav_vi Feb 10 '14 at 14:28
  • @mbratch At that point, `feof` will only be nonzero if no error occured. For example, if `fgets` encountered an i/o error during the read, `fgets` will return `NULL` and `feof` will return zero. – Brandin Feb 10 '14 at 14:38
  • @Brandin yes, that is true. There still needs to be a check for error. – lurker Feb 10 '14 at 14:47
  • "it's only going to return a `NULL` if it's actually at `EOF` when called" Small clarification: If a rare input error occurs, `NULL` is also returned - so some characters may have been read prior to or as part of the error. – chux - Reinstate Monica Dec 14 '17 at 19:11