【问题标题】:How does feof() actually know when the end of file is reached?feof() 如何真正知道何时到达文件末尾?
【发布时间】:2016-05-12 17:40:32
【问题描述】:

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

我意识到这可能是某个地方的重复问题,但我一直无法找到它,可能是因为我不知道最好的表达方式。如果已经有答案,将不胜感激。谢谢。

【问题讨论】:

  • 图像流没有已知的限制。然后,没有其他方法可以尝试读取文件结尾。 (注意文件流是对不同类型的输入/输出的抽象)
  • @kartik7153 没有回答他的问题。他询问操作系统如何具体决定何时到达文件末尾。
  • @Jake 这是一个操作系统细节(它取决于使用的底层设备/协议)
  • 我想说,文件系统的实现最终会决定,并且由于 Linux 支持不同的文件系统,因此需要查找它们的实现代码。

标签: c++ c feof


【解决方案1】:

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

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

【讨论】:

  • 谢谢,这正是我想知道的!
【解决方案2】:

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

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

一个有趣的问题是当流结束时会发生什么,但尚未被任何读取检测到。例如,如果您打开一个空文件。在任何fread() 之前第一次调用feof() 是否返回真或假?答案很可能是错误的。 The docs 在这个问题上不是很清楚:

该指标通常由流上的先前操作设置 试图在文件结尾处或之后读取。

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

【讨论】:

  • 谢谢。您能否详细说明“如果在缓冲时 fread() 检测到 EOF”?我想要了解的是 EOF 的实际标记方式以及检测方式。
【解决方案3】:

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

【讨论】:

  • 我认为这最接近我的要求。因此,当您执行FILE *fp = fopen() 时,FILE 类型是否已经知道文件的大小,然后feof() 可以使用?
  • 通常,是的。元信息的一部分是文件大小,(还有日期、位置、以前的版本等)。元信息与文件一起存储并在文件打开时检索。但文件系统实际上并不需要它,所以这取决于它是如何实现的。
【解决方案4】:

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

【讨论】:

  • 不错的代码 sn-p。但是你的意思是最后一个字符不一定是已知的吗?
  • @JohnSmithSr。是的,修改后的帖子,你是“不”对的。
【解决方案5】:

feof() 函数在读取 EOF 字符时设置文件结束指示符。所以当feof() 读取最后一项时,EOF 一开始并没有随之读取。由于没有设置 EOF 指示符并且feof() 返回零,因此流程再次进入 while 循环。这次fgets 知道下一个字符是 EOF,它会丢弃它并返回 NULL,但还会设置 EOF 指示符。所以feof() 检测到文件结束指示符并返回一个非零值,从而中断了 while 循环。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-08
    • 1970-01-01
    • 2019-12-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多