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

时间:2016-05-12 17:40:32

标签: c++ c feof

我是C ++的初学者,并试图更好地理解feof()。我已经读过feof()标志只有在尝试读取文件末尾之后才会设置为true,这样很多次初学者会比他们期望的那样多读一次while(!feof(file))。我试图理解的是,它实际上是如何解释已经尝试读取文件的末尾?整个文件是否已经读入并且已知的字符数或是否有其他机制在起作用?

我意识到这可能是某个地方的重复问题,但我一直无法找到它,可能是因为我不知道说出我要问的最佳方法。如果已经有答案,那么链接将非常受欢迎。谢谢。

5 个答案:

答案 0 :(得分:10)

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

当做出该决定时,它会向上传递。最终,它进入标准库,在内部记录已到达文件末尾。当对库的读取请求尝试超过该记录的结束时,将设置EOF标志并且feof将开始返回true。

答案 1 :(得分:8)

feof()是标准C库缓冲 I / O的一部分。由于它是缓冲的,fread()预先读取一些数据(绝对整个文件)。如果在缓冲时fread()检测到EOF(底层OS例程返回一个特殊值,通常为-1),则会在FILE结构上设置一个标志。 feof()只是检查该标志。因此,feof()返回true实质上意味着“先前的读取尝试遇到文件结束”。

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

一个有趣的问题是当流结束时会发生什么,但是任何读取都没有检测到。例如,如果您打开一个空文件。在任何feof()返回true或false之前,第一次调用fread()吗?答案可能是错误的。 The docs在这个问题上并不十分明确:

  

此指标通常由流上的先前操作设置   尝试在文件结尾处读取或超过文件结尾。

听起来好像某个特定的实现可能会选择其他一些不寻常的方法来设置此标志。

答案 2 :(得分:3)

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

答案 3 :(得分:3)

  

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() %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;
}

输出

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

答案 4 :(得分:-2)

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