13

I opened a file, the stream is found at the address of pointer ptr. I am attempting to see whether or not a file is blank. Using the following

if (fgetc(ptr) != EOF)

works as expected. When the file is blank, the statement is not executed. When the file is not blank, the statement is not executed.

However, using

if (!feof(ptr))

always executes the statement.

Why does this happen? Is there a way to use the feof function?

lost_in_the_source
  • 10,998
  • 9
  • 46
  • 75

5 Answers5

15

Is there a way to use the feof function?

Yes, there is. After an input function has returned a value that indicates that it has no more input to process, you can call feof() and/or ferror() to determine whether the condition was caused by reaching the end of the input or some error condition.

This is the only valid use for the feof() and ferror() functions.

The result that indicates that no more input remains differs from one function to another. For example fgetc() returns the value EOF, fgets() returns a null pointer, and fread() returns some value less than the number of records requested. You need to read the documentation for each input function to see how it works.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • `fread` returning less than requested only indicates the equivalent of EOF when 0 was returned. – Weather Vane Mar 22 '16 at 21:00
  • 1
    @WeatherVane: I don't believe that's correct. Are you thinking of `read()`? Quoting the C standard: "The **`fread`** function returns the number of elements successfully read, which may be less than **`nmemb`** if a read error or end-of-file is encountered." – Keith Thompson Mar 22 '16 at 21:01
  • Nope. A non-0 value returned is not EOF, it was a valid read. A 0 value returned was EOF. – Weather Vane Mar 22 '16 at 21:02
  • Well yes, but is not equivalent to the EOF returned by say `getchar`, which attempted to read past the end of the file. Perhaps I am just nitpicking. – Weather Vane Mar 22 '16 at 21:04
  • 1
    @WeatherVane: It pretty much is equivalent. `fread()` works as if by repeated calls to `fgetc()`. If you request `fread()` to read, say, 10 records and it returns 5, that means it tried and failed to read the 6th record, either because there was an error or because it reached the end of the input stream. – Keith Thompson Mar 22 '16 at 21:06
  • 3
    For EOF or error `fgetc` returns EOF; `fgets` returns null pointer; `fread` returns – dave_thompson_085 Mar 23 '16 at 09:30
10

In C standard library "end of file" is not an independent self-detecting condition. "End of file" is merely a file state set by the preceding read operation. Until you perform a read operation and until that read operation bumps into the actual end of file, "eof" state is not set and feof() returns 0.

Many (most) standard I/O functions in C standard library already return some completion code that can be used to detect "end of file" condition (like fgetc() returning EOF value). This means that most of the time calling feof() is unnecessary.

In other words, if you think about "end of file" as a brick wall, in C standard library it is not sufficient to stand right in front of that wall in order to detect it. It is necessary to actually bump into that wall with your forehead in order to discover its presence.

When you open an empty file, you begin in a state "right before the wall". At this point "end of file" condition is not detected yet. You have to try reading something in order to crash into that wall and thus detect its presence.

The reasons for this design are perfectly logical. C language is designed to support various input streams and file systems, including 1) file systems that do not store file sizes at all, as well as 2) file systems that know only approximate file size (like rounded to a nearest cluster). In such file systems the end of file is usually designated by a special marker. You don't know where that marker is until you run into it while reading the file. (For the very same reason C streams do not guarantee support for positioning from SEEK_END origin in fseek function.)

As @Barmar noted in the comments, an even better example of an input stream without a pre-determined "end of file" position is standard input linked to terminal.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 2
    To add to your reasons, it might not be reading from a filesytem at all. It could be reading from a terminal, pipe, network stream, etc. These don't have file sizes. – Barmar Mar 22 '16 at 21:18
  • This is a detailed explanation, but you should expand on the distinction between input failure and end of file condition: when a read operation fails, such as `fgetc() == EOF`, this can mean end-of-file reached or some kind of read error. `feof()` can be used then to confirm if end-of-file caused the failure. This should be its only purpose. – chqrlie Mar 23 '16 at 00:25
7

feof(f) will always return false right after you open the file because if gives you the value of a flag which is initialized to false and set to true only after at least one reading operation fails.

jdarthenay
  • 3,062
  • 1
  • 15
  • 20
3

You could use feof like this:

fgetc(fp);
if (feof(fp))
  ...

feof checks an end-of-file indicator set after a read that attempted to read at or past the end of file. It is useful in situations where code that you don't control was reading from the file, and you want to stop the processing once the file has been exhausted.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • 1
    Downvoted because newbies should not be encouraged to use `feof()` **even** with a correct explanation. What is wrong with `if (fgetc(fp) == EOF)`? – chqrlie Mar 22 '16 at 20:51
  • 3
    A problem with this is that `fgetc` returning EOF indicates any error condition, but `feof` only checks the specific condition of end-of-file. So if there was a read error but the file is not ended then your code will go on to act on bogus data. – M.M Mar 22 '16 at 20:56
  • I agree. If you say `if (fgetc(fp) == EOF)` and assume that it means the file was empty then you have made a big mistake because empty files might have some semantic meaning to your program. If you need to know the file is empty then you really do need to call `feof()` so I don't think encouraging newbies to write programs that assume a read error means that the file is empty is necessarily a good thing. – Jerry Jeremiah Mar 22 '16 at 21:12
  • @JerryJeremiah the fact that the file was opened succesfully, but contained no data will tell you that. `feof` does *not* tell you the file is empty. It tells you you have attempted to read past the end of the file, which is what `EOF` tells you anyway. Please read the man page for `feof`, it is one of the most frequent misunderstandings. – Weather Vane Mar 22 '16 at 21:26
  • 1
    I understand how it works - telling me to read the man page is condescending. Opening a file means the file has been found and does NOT mean the sector containing the data has a valid checksum. Assuming that a read error means that the file is empty is asking for trouble if empty files have a semantic meaning. If knowing the file is empty or not is important then you need to actually test that specific condition. In an embedded system where the program cannot stop under any circumstances having a program work correctly under every circumstance is very important. – Jerry Jeremiah Mar 22 '16 at 21:48
  • 1
    @chqrlie Aside from the problem of `fgetc(fp) == EOF` producing false positives by conflating EOF with other read errors, the answer explains what `feof` does in the context of the question, without necessarily endorsing its for that particular case. The last sentence is the important bit, explaining why `feof` is actually useful, which was not obvious to the OP. – user4815162342 Mar 22 '16 at 22:10
  • @user4815162342: I understand your stance about read error. I knew someone would come up with this counter argument. Yet you did not write anything about this distinction in your answer. There is something fishy about `fgetc()` retuning `EOF` upon read error, the `EOF` value is obviously misnamed, but there is something much more fishy about `feof()`: the function is so commonly misused, it must have counter-intuitive semantics. I believe it is more harmful to condone the naked use of `feof()` than letting newbies erroneously believe that `EOF` means *end-of-file* instead of *error-condition* – chqrlie Mar 23 '16 at 00:16
  • 1
    @chqrlie The EOF vs error argument is only part of my comment (*aside from...*) which I personally agree is not all that important. Consider that `feof` being "commonly misused" doesn't mean we can't explain to a beginner what it does. The OP *explicitly* asked how to use `feof`, and the answer explains it. Is there a problem in the answer *other* than your general dislike of `feof`? – user4815162342 Mar 23 '16 at 07:11
0

Late answer, however to add a helpful quote from Programming in C (4th Edition) by Stephen Kochan, p. 367:

Remember, feof() tells you that an attempt has been made to read past the end of a file, which is not the same as telling you that you just read the last data item from a file. You have to read one past the last data item for feof() to return nonzero.

Italics mine.

S3DEV
  • 8,768
  • 3
  • 31
  • 42