== EOF和feof的区别让人困惑

13

我打开了一个文件,流被找到在指针ptr的地址上。我试图查看文件是否为空。使用以下代码:

if (fgetc(ptr) != EOF)

正常工作。当文件为空时,语句不会被执行。当文件不为空时,语句也不会被执行。

然而,使用

if (!feof(ptr))

始终执行该语句。

为什么会发生这种情况?有没有办法使用 feof 函数?


7
在读取失败后设置EOF指示器。 - lost_in_the_source
5个回答

15

有没有一种方法可以使用 feof 函数?

是的,有。在输入函数返回指示它没有更多输入要处理的值之后,您可以调用 feof() 和/或 ferror() 以确定该条件是否是由于到达输入结束还是某些错误条件导致的。

这是 feof()ferror() 函数的唯一有效用法。

指示没有更多输入剩余的结果因函数而异。例如,fgetc() 返回值为 EOFfgets() 返回空指针,fread() 返回小于所请求记录数的某个值。您需要阅读每个输入函数的文档以了解其工作原理。


fread返回少于请求的数据时,这只表示相当于EOF的情况,当返回0时。 - Weather Vane
1
@WeatherVane:我不相信那是正确的。你是在想read()吗?引用C标准:“**fread函数返回成功读取的元素数量,如果遇到读取错误或文件结束,则可能小于nmemb**。” - Keith Thompson
不是的。返回非0值并不代表EOF,它是一个有效的读取。返回0值才表示EOF。 - Weather Vane
是的,但它并不等同于getchar返回的EOF,因为getchar试图读取超出文件末尾的内容。也许我只是在挑刺。 - Weather Vane
1
@WeatherVane:它基本上是等效的。fread() 的工作方式就像重复调用 fgetc()。如果您请求 fread() 读取,比如说,10 条记录,但它只返回了 5 条,那么这意味着它尝试并未能读取第 6 条记录,可能是因为出现了错误或者已经到达了输入流的末尾。 - Keith Thompson
3
对于EOF或错误,fgetc返回EOF; fgets返回空指针; fread返回<N。 - dave_thompson_085

11
在C标准库中,“文件结束”不是独立的自我检测条件。 “文件结束”仅仅是由前面的读取操作所设定的文件状态。在执行读取操作之前,直到该读取操作遇到实际的“文件结束”为止,“eof”状态尚未设置,并且feof()返回0。
在C标准库中,许多(大多数)标准I/O函数已经返回一些可用于检测“文件结束”条件的完成代码(例如fgetc()返回EOF值)。这意味着大多数情况下,调用feof()是不必要的。
换句话说,如果你把“文件结束”看作是一堵墙,在C标准库中,站在那堵墙前面是不足以检测到它的。必须用前额撞到那面墙才能发现其存在。
当你打开一个空文件时,你处于一个“紧靠着墙”的状态。此时尚未检测到“文件结束”条件。你必须尝试读取一些内容才能撞到那面墙并检测到其存在。
这种设计的原因是非常合理的。C语言旨在支持各种输入流和文件系统,包括1)根本不存储文件大小的文件系统,以及2)只知道近似文件大小的文件系统(如四舍五入到最近的簇)。在这样的文件系统中,文件结束通常由一个特殊标记指定。在读取文件时,您不知道那个标记在哪里,直到您遇到它为止。(出于同样的原因,C流不保证支持fseek函数中从SEEK_END起点开始的定位。)
正如@Barmar在评论中指出的那样,没有预定“文件结束”位置的输入流的更好示例是链接到终端的标准输入。

2
另外,可能并非从文件系统读取数据。它可能是从终端、管道、网络流等中读取的,这些没有文件大小的概念。 - Barmar
这是一个详细的解释,但你应该扩展一下输入故障和文件结束条件之间的区别:当读取操作失败时,比如 fgetc() == EOF,这可能意味着已经到达了文件结尾或者出现了某种读取错误。可以使用 feof() 来确认是否是因为文件结束导致了失败。这应该是它唯一的目的。 - chqrlie

7

feof(f)函数返回的总是false,因为在你打开文件后,该函数会给你一个已初始化为false的标记(flag)的值。只有在至少一次读取操作失败之后,该标记才会被设置为true。


3
您可以像这样使用feof
fgetc(fp);
if (feof(fp))
  ...

feof函数用于检查文件指针位置是否已到达文件结尾,如果是则返回非零值。对于那些你无法控制的读取文件的代码而言,该函数能够帮助你判断何时停止处理。


1
因为即使有正确的解释,也不应该鼓励新手使用feof()。使用if (fgetc(fp) == EOF)有什么问题吗? - chqrlie
3
这个问题在于 fgetc 返回 EOF 表示任何错误条件,但是 feof 只检查文件结束的特定条件。因此,如果存在读取错误但文件未结束,则您的代码将继续对虚假数据进行操作。 - M.M
1
我明白它是如何工作的 - 让我去读手册是有失尊重的。打开一个文件意味着找到了该文件,并不意味着包含数据的扇区具有有效的校验和。假设读取错误意味着文件为空,如果空文件具有语义含义,则会引发问题。如果知道文件是否为空很重要,那么您需要实际测试该特定条件。在嵌入式系统中,程序无法在任何情况下停止,使程序在每种情况下都能正确工作非常重要。 - Jerry Jeremiah
1
@chqrlie 除了“fgetc(fp) == EOF”会将EOF与其他读取错误混淆导致误报之外,该答案还解释了在问题的背景下feof的作用,但并不一定认可它在特定情况下的使用。最后一句话是重点,解释了为什么feof实际上很有用,这对OP来说并不明显。 - user4815162342
1
@chqrlie,EOF与错误参数只是我的评论的一部分(*除此之外...*),我个人认为这并不是非常重要。请考虑,即使feof被普遍误用,我们仍然可以向初学者解释它的作用。OP明确地询问如何使用feof,而答案对此进行了解释。除了您对feof的普遍反感外,答案还存在其他问题吗? - user4815162342
显示剩余3条评论

0

虽然回答晚了,但是我想引用 Stephen Kochan 在他的书 Programming in C (第四版) 第367页中的一句话来帮助你:

记住,feof() 告诉你已经尝试读取超过文件末尾,这与告诉你刚刚读取了文件的最后一个数据项是不同的你必须再读取一次最后一个数据项,才能使 feof() 返回非零值。

以上为个人意见。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接