feof()
函数。据我所知,只有在试图多次读取文件末尾后,feof()
标志才会设置为true,因此如果像while(!feof(file))
这样做,许多初学者将读取比他们预期的多一次。我想了解的是,它实际上如何解释已经尝试读取超过文件结尾?整个文件是否已经被读入,并且已知字符数,还是有其他机制在起作用?我意识到这可能是重复的问题,但我找不到它,可能是因为我不知道询问的最佳方式。如果已经有答案,那么链接将不胜感激。谢谢。
feof()
函数。据我所知,只有在试图多次读取文件末尾后,feof()
标志才会设置为true,因此如果像while(!feof(file))
这样做,许多初学者将读取比他们预期的多一次。我想了解的是,它实际上如何解释已经尝试读取超过文件结尾?整个文件是否已经被读入,并且已知字符数,还是有其他机制在起作用?feof
就会开始返回真值。feof()
是标准C库中的一部分,用于缓冲I/O。由于它是缓冲的,fread()
会预读取一些数据(肯定不是整个文件)。如果在缓冲时,fread()
检测到EOF(底层操作系统例程返回一个特殊值,通常是-1
),它会在FILE
结构上设置一个标志。 feof()
只是检查该标志。因此,feof()
返回true基本上意味着“先前的读取尝试遇到了文件结尾”。
如何检测EOF是特定于操作系统/文件系统的,与C库/语言没有任何关系。操作系统具有从文件中读取数据的某些接口。 C库只是程序与操作系统之间的桥梁,因此如果您更改操作系统,则无需更改程序。操作系统知道如何在其文件系统中存储文件,因此知道如何检测EOF。 我猜通常是通过将当前位置与文件长度进行比较来执行此操作,但这可能并不容易,并且可能涉及许多低级细节(例如,如果文件位于网络驱动器上怎么办?)。
一个有趣的问题是当流已经结束,但尚未被任何读取操作检测到时会发生什么。例如,如果您打开一个空文件。在任何fread()
之前,第一次调用feof()
会返回true还是false?答案可能是false。文档在这个主题上并不十分清楚:
听起来好像某个特定实现可能会选择其他不寻常的方式来设置此标志。此指示通常由先前在流上尝试读取或超过文件结尾的操作设置。
大多数文件系统都保存有关文件的元信息(包括其大小),尝试读取超出文件末尾的部分会导致设置feof标志。其他一些旧的或轻量级文件系统在到达链中最后一个块的最后一个字节时设置feof。
FILE *fp = fopen()
时,FILE类型是否已经知道文件的大小,可以被 feof()
使用? - Austinfeof()如何知道文件何时结束?
当代码试图读取超过最后一个字符时。
根据文件类型,最后一个字符不一定在尝试读取它之前就已知,只有在尝试读取它并且没有字符可用时才会知道。
演示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循环。