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

5

7

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.

Rhiamon answered 12/5, 2016 at 17:40 Comment(8)
https://mcmap.net/q/239544/-end-of-file-eof-in-c Hope this helps :)Ovule
Image a stream with has no known limit. Then, there is no other way then making a read attempt to get the end of file. (Note file streams are an abstraction for a varying kind of input/output)Alvinia
@Ovule that does not answer his question tho. He asks how specifically the OS decides when end of file was reached.Kaylyn
@Dieter But what actually happens when a read attempt is made and there are no characters left in the file to read? I guess that's pretty much what I'm trying to figure out.Rhiamon
@Jake At that point (and only at that point) the end of file is indicated.Alvinia
@Jake That is an operating system detail (it depends on the underlaying device/protocol in use)Alvinia
Ah okay. Is there any general information someone can point me to on how it is does in Linux? I'm interested in embedded systems so I'd like to really understand this at the lowest level I can.Rhiamon
I'd say in the very end the file-systems implementation decides, and as Linux supports different file-systems one needs to look up their implementations code.Rrhoea
M
11

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.

Modestamodeste answered 12/5, 2016 at 17:59 Comment(1)
Thanks this is exactly what I wanted to know!Rhiamon
L
8

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.

Limy answered 12/5, 2016 at 17:53 Comment(1)
Thanks. Could you please elaborate more on, "If, while buffering, fread() detects EOF"? What I'm trying to get at is how EOF is actually marked and how it is detected.Rhiamon
R
3

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.

Rimarimas answered 12/5, 2016 at 17:56 Comment(2)
I think this is closest to getting at what I'm asking. So when you do FILE *fp = fopen() does the FILE type already know the size of the file which can then be used by feof()?Rhiamon
Usually, yes. Part of the meta information is the file size, (also has dates, locations, previous versions etc). The meta information is stored with the file and retrieved at the time the file is opened. But it is not actually required of the file system, so it depends on how it was implemented.Rimarimas
E
2

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
Egghead answered 12/5, 2016 at 19:3 Comment(2)
Nice code snippet. But do u mean the last character is NOT necessarily known?Lipson
@JohnSmithSr. Yes, post amended, you are "not" right.Egghead
I
-2

The feof() function sets the end of file indicator when the EOF character is read. So when feof() reads the last item, the EOF is not read along with it at first. Since no EOF indicator is set and feof() returns zero, the flow enters the while loop again. This time fgets comes to know that the next character is EOF, its discards it and returns NULL but also sets the EOF indicator. So feof() detects the end of file indicator and returns a non-zero value therefore breaking the while loop.

Interloper answered 13/5, 2016 at 15:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.