我是 C++ 的初学者,并试图更好地理解feof()
. 我已经读过,feof()
只有在尝试读取文件末尾之后,该标志才设置为 true,因此初学者会读一次,如果他们执行类似while(!feof(file))
. 不过,我想了解的是,它实际上如何解释已尝试读取文件末尾的内容?整个文件是否已经读入并且字符数已经知道,还是有其他机制在起作用?
我意识到这可能是某个地方的重复问题,但我一直无法找到它,可能是因为我不知道表达我所问内容的最佳方式。如果已经有答案,将不胜感激。谢谢。
无论 C++ 库做什么,最终它都必须从文件中读取。在操作系统的某个地方,有一段代码最终会处理该读取。它从文件系统获取文件的长度,以与文件系统存储其他所有内容相同的方式存储。知道了文件的长度、读取的位置以及要读取的字节数,就可以判断低级读取命中了文件的末尾。
当做出该决定时,它会向上传递到堆栈中。最终,它到达标准库,该标准库在内部记录已到达文件末尾。当对库的读取请求尝试越过记录结束时,EOF 标志被设置feof
并将开始返回 true。
feof()
是标准 C 库缓冲I/O 的一部分。由于它是缓冲的,因此会fread()
预先读取一些数据(但绝对不是整个文件)。如果在缓冲时fread()
检测到 EOF(底层 OS 例程通常返回一个特殊值),它会在结构-1
上设置一个标志。只需检查该标志。所以返回 true 本质上意味着“先前的读取尝试遇到文件结尾”。FILE
feof()
feof()
如何检测 EOF 是特定于 OS/FS 的,与 C 库/语言无关。操作系统有一些接口可以从文件中读取数据。C 库只是操作系统和程序之间的桥梁,因此如果您迁移到另一个操作系统,则无需更改程序。操作系统知道文件如何存储在其文件系统中,因此它知道如何检测 EOF。我的猜测是,通常它是通过将当前位置与文件的长度进行比较来执行的,但这可能并不那么容易,并且可能涉及很多低级细节(例如,如果文件在网络驱动器上怎么办? ?)。
一个有趣的问题是当流结束时会发生什么,但尚未被任何读取检测到。例如,如果您打开一个空文件。feof()
在任何fread()
返回 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() %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
该feof()
函数在读取 EOF 字符时设置文件结束指示符。因此,当feof()
读取最后一项时,EOF 最初不会与它一起读取。由于没有设置 EOF 指示符并feof()
返回零,因此流程再次进入 while 循环。这次fgets
知道下一个字符是 EOF,它会丢弃它并返回 NULL,但还会设置 EOF 指示符。因此feof()
检测文件结束指示符并返回一个非零值,从而中断 while 循环。