feof()实际上是如何知道何时到达文件末尾的

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

本文关键字:文件 何时 何知道 实际上 feof      更新时间:2023-10-16

我是C++的初学者,正在努力更好地理解feof()。我读到feof()标志只有在尝试读取超过文件末尾的次数后才设置为true,因此初学者如果执行类似while(!feof(file))的操作,将比预期多读取一次。不过,我试图理解的是,它实际上是如何解释试图读取文件末尾之后的内容的?整个文件是否已经读入,字符数是否已知,或者是否有其他机制在起作用?

我意识到这可能是一个重复的问题,但我一直找不到,可能是因为我不知道用什么最好的方式来表达我的问题。如果已经有答案,请联系我们。谢谢

无论C++库做什么,最终都必须从文件中读取。在操作系统的某个地方,有一段代码最终处理读取。它从文件系统获得文件的长度,以与文件系统存储其他内容相同的方式存储。知道文件的长度、读取的位置和要读取的字节数,就可以确定低级别读取到达了文件的末尾。

当做出该决定时,它被向上传递到堆栈中。最终,它到达标准库,该库在内部记录已经到达文件末尾。当对库的读取请求试图通过记录的结束时,EOF标志被设置,feof将开始返回true。

feof()是标准C库缓冲I/O的一部分。由于它是缓冲的,fread()预读取一些数据(当然不是整个文件的)。如果在缓冲时,fread()检测到EOF(底层操作系统例程返回一个特殊值,通常为-1),它会在FILE结构上设置一个标志。feof()只是简单地检查该标志。所以feof()返回true本质上意味着"以前的读取尝试遇到了文件末尾"。

EOF的检测方式是特定于OS/FS的,与C库/语言无关。操作系统有一些从文件中读取数据的接口。C库只是操作系统和程序之间的一座桥梁,因此如果您转到另一个操作系统,就不必更改程序。操作系统知道文件是如何存储在其文件系统中的,因此它知道如何检测EOF。我的猜测是,通常情况下,它是通过将当前位置与文件的长度进行比较来执行的,但这可能并不那么容易,并且可能涉及许多低级细节(例如,如果文件在网络驱动器上呢?)。

一个有趣的问题是,当流处于末尾,但尚未被任何读取检测到时,会发生什么。例如,如果打开一个空文件。在任何fread()之前对feof()的第一次调用返回true还是false?答案可能是错误的。文档对这个主题不是很清楚:

此指示符通常由流上的先前操作设置试图在文件末尾或超过文件末尾进行读取的。

听起来,一个特定的实现可能会选择一些其他不寻常的方式来设置这个标志。

大多数文件系统都会维护有关文件的元信息(包括文件大小),并且在设置feof标志时尝试读取超过末尾的结果。其他文件系统,例如旧的或轻量级的文件系统,在到达链中最后一个块的最后一个字节时设置feof。

feof()实际上是如何知道何时到达文件末尾的?

当代码试图读取时,传递最后一个字符。

根据文件类型的不同,最后一个字符不一定是已知的,直到尝试读取该字符,并且没有可用的字符。


演示feof()从0到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() %dn", feof(istream));
    printf("fread  %zun", fread(buf, 1, 10, istream));
    printf("feof() %dn", feof(istream));
    printf("fread  %zun", fread(buf, 1, 10, istream));
    printf("feof() %dn", feof(istream));
    puts("");
    fclose(istream);
  }
}
int main(void) {
  ftest(9);
  ftest(10);
  return 0;
}

输出

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

feof()函数在读取EOF字符时设置文件结束指示符。因此,当feof()读取最后一个项目时,EOF最初不会与它一起读取。由于没有设置EOF指示器,并且feof()返回零,因此流程再次进入while循环。这一次fgets知道下一个字符是EOF,它会丢弃它并返回NULL,但也会设置EOF指示符。因此,feof()检测到文件结束指示符并返回一个非零值,从而打破while循环。