Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does feof() actually know when the end of file is reached?

Tags:

c++

c

feof

I'm a beginner in C++ and trying to better understand feof(). I've read that feof() flag is set to true only after trying to read past the end of a file so many times beginners will read once more than they were expecting if they do something like while(!feof(file)). What I'm trying to understand though, is how does it actually interpret that an attempt has been made to read past the end of the file? Is the entire file already read in and the number of characters already known or is there some other mechanism at work?

I realize this may be a duplicate question somewhere, but I've been unable to find it, probably because I don't know the best way to word what I'm asking. If there is an answer already out there a link would be much appreciated. Thanks.

like image 766
Austin Avatar asked May 12 '16 17:05

Austin


4 Answers

Whatever else the C++ library does, eventually it has to read from the file. Somewhere in the operating system, there is a piece of code that eventually handles that read. It obtains from the filesystem the length of the file, stored the same way the filesystem stores everything else. Knowing the length of the file, the position of the read, and the number of bytes to be read, it can make the determination that the low-level read hits the end of the file.

When that determination is made, it is passed up the stack. Eventually, it gets to the standard library which records internally that the end of file has been reached. When a read request into the library tries to go past that recorded end, the EOF flag is set and feof will start returning true.

like image 105
David Schwartz Avatar answered Sep 22 '22 21:09

David Schwartz


feof() is a part of the standard C library buffered I/O. Since it's buffered, fread() pre-reads some data (definitely not the whole file, though). If, while buffering, fread() detects EOF (the underlying OS routine returns a special value, usually -1), it sets a flag on the FILE structure. feof() simply checks for that flag. So feof() returning true essentially means “a previous read attempt encountered end of file”.

How EOF is detected is OS/FS-specific and has nothing to do whatsoever with the C library/language. The OS has some interface to read data from files. The C library is just a bridge between the OS and the program, so you don't have to change your program if you move to another OS. The OS knows how the files are stored in its filesystem, so it knows how to detect EOF. My guess is that typically it is performed by comparing the current position to the length of the file, but it may be not that easy and may involve a lot of low-level details (for example, what if the file is on a network drive?).

An interesting question is what happens when the stream is at the end, but it was not yet detected by any reads. For example, if you open an empty file. Does the first call to feof() before any fread() return true or false? The answer is probably false. The docs aren't terribly clear on this subject:

This indicator is generally set by a previous operation on the stream that attempted to read at or past the end-of-file.

It sounds as if a particular implementation may choose some other unusual ways to set this flag.

like image 29
Sergei Tachenov Avatar answered Sep 23 '22 21:09

Sergei Tachenov


Most file system maintain meta information about the file (including it's size), and an attempt to read past the end of results in the feof flag being set. Others, for instance, old or lightweight file systems, set feof when they come to the last byte of the last block in the chain.

like image 28
Gregg Avatar answered Sep 21 '22 21:09

Gregg


How does feof() actually know when the end of file is reached?

When code attempts to read passed the last character.

Depending on the file type, the last character is not necessarily known until a attempt to read past it occurs and no character is available.


Sample code demonstrating feof() going from 0 to 1

#include <stdio.h>

void ftest(int n) {
  FILE *ostream = fopen("tmp.txt", "w");
  if (ostream) {
    while (n--) {
      fputc('x', ostream);
    }
    fclose(ostream);
  }
  FILE *istream = fopen("tmp.txt", "r");
  if (istream) {
    char buf[10];
    printf("feof() %d\n", feof(istream));
    printf("fread  %zu\n", fread(buf, 1, 10, istream));
    printf("feof() %d\n", feof(istream));
    printf("fread  %zu\n", fread(buf, 1, 10, istream));
    printf("feof() %d\n", feof(istream));
    puts("");
    fclose(istream);
  }
}

int main(void) {
  ftest(9);
  ftest(10);
  return 0;
}

Output

feof() 0
fread  9  // 10 character read attempted, 9 were read
feof() 1  // eof is set as previous read attempted to read passed the 9th or last char
fread  0
feof() 1

feof() 0
fread  10  // 10 character read attempted, 10 were read
feof() 0   // eof is still clear as no attempt to read passed the 10th, last char
fread  0
feof() 1
like image 38
chux - Reinstate Monica Avatar answered Sep 21 '22 21:09

chux - Reinstate Monica