C语言中的库函数feof和ferror
我们来详细解释一下 C 语言中的 ferror 和 feof 这两个库函数。
1. ferror 函数
int ferror(FILE *stream);
功能:检查指定文件流(stream)上是否发生了错误。
返回值:
如果文件流上有错误发生,返回一个非零值(true)。
如果没有错误发生,返回 0(false)。
说明:
当对文件进行读写操作时,如果发生错误(例如:磁盘已满、权限问题、硬件故障等),文件流会记录一个错误标志。
ferror 函数就是用来检查这个错误标志的。
一旦错误标志被设置,它将一直保持,直到调用 clearerr() 函数清除该标志,或者关闭文件。
我们下面举一个例子,看看会不会报错,假设有该文件,然后我们以只读打开,再写入内容
#include <stdio.h>
int main()
{
FILE* file = fopen("nonexistent.txt", "r");
if (file == NULL)
{
perror("Error opening file");
return 1;
}
char buffer[7] = {"abcdef"};
if (fputs(buffer,file) == EOF)
{
if (ferror(file))
{
printf("Error reading to file.\n");
}
}
fclose(file);
file = NULL;
return 0;
}

我们看到果然报错了,只读情况下不可以写入(vs编译器中),所以在运行fputs时,fputs返回了一个EOF并且设置了一个错误的指示,当然这个指示在文本中是看不到的,下面是fputs返回值的原文。
On error, the function returns EOF and sets the error indicator (ferror).
2. feof 函数
int feof(FILE *stream);
功能:检查指定文件流(stream)是否已经到达文件末尾(End-of-File)。
返回值:
如果文件流已经到达文件末尾,返回一个非零值(true)(这个说法其实并不准确,我下面会做进一步解释)。
如果还没有到达文件末尾,返回 0(false)。
A non-zero value is returned in the case that the end-of-file indicator associated with the stream is set.
Otherwise, zero is returned.
说明:
当文件指针移动到文件末尾时,文件流会记录一个 EOF 标志。
feof 函数就是用来检查这个 EOF 标志的。
注意:feof 只有在文件读取操作尝试越过文件末尾时才会返回 true。也就是说,在文件末尾的前一个字符被读取后,此时使用feof 仍然返回 false,只有当再次尝试读取时(此时会失败),feof 才会返回 true。
这是最核心的一点。其实文件本身的末尾并没有一个叫做 "EOF" 的特殊字符。
每个打开的文件流(FILE *)内部都维护着一个文件位置指示器。
你可以把它想象成一个指向文件内容的光标。
当你打开一个文件时,这个指示器通常指向文件的开头。
当你调用 fgetc()、fgets() 等读取函数时,它们会从指示器当前的位置读取数据,然后将指示器向后移动。
而feof 函数的作用是检查一个内部标志位,我们称之为 "end-of-file indicator"。这个标志位只有在特定情况下才会被设置。
让我们通过一个具体的例子来一步步说明:
假设我们有一个文件 test.txt,内容非常简单:
ABC\n
这个文件包含 3 个字符 'A', 'B', 'C',以及一个隐含的换行符 \n,总共 4 个字符。
然后我们设定一个流指针stream指向该流。
文件位置指示器的移动过程如下:
初始状态:
文件位置指示器:指向 A 的前面。
feof(stream): false
第一次调用 fgetc(stream):
函数从指示器位置读取字符 'A'。
文件位置指示器向后移动一位,现在指向 B 的前面。
函数返回 'A'
feof(stream): 仍然是 false,因为我们只是读取到了文件中的一个有效字符,并没有尝试越过文件末尾。
第二次调用 fgetc(stream):
读取字符 'B'。
指示器指向 C 的前面。
返回 'B'。
feof(stream): false。
第三次调用 fgetc(stream):
读取字符 'C'。
指示器指向换行符 \n 的前面。
返回 'C'。
feof(stream): false。
第四次调用 fgetc(stream):
读取字符 '\n'。
指示器向后移动,现在它指向了文件的物理末尾之后,也就是再往后没东西了。
返回 '\n'。
feof(stream): 仍然是 false!
因为虽然指示器已经越过了最后一个字符,但我们这次读取操作本身是成功的,我们成功地读到了 \n。
而feof 只有在读取失败并且失败的原因是:已经到达文件末尾时,才会为 true。
第五次调用 fgetc(stream):
函数尝试从当前指示器位置(文件末尾之后)读取一个字符。
这次尝试失败了,因为已经没有更多的数据可以读取了。
作为失败的标志,函数返回 EOF (即 -1)。
同时,在函数内部,它会设置文件流的 "end-of-file indicator"。
feof(stream): 现在变成了 true,因为它检测到这个标志位已经被设置。
总的来说feof是当流的读取发生错误时,判断错误是否为已经读取到文件末尾,因为在文件读取到末尾时使用feof依旧返回值为0,只有当读取失败,并且失败原因为达到文件末尾,才会返回大于0的数
说白了就是看流的结构体中end-of-file indicator有没有被设置
这其实根一些读取字符的函数的返回逻辑有关,我们拿fgetc举例
On success, the character read is returned (promoted to an int value).
The return type is int to accommodate for the special value EOF, which indicates failure:
If the position indicator was at the end-of-file, the function returns EOF and sets the eof indicator (feof) of stream.
If some other reading error happens, the function also returns EOF, but sets its error indicator (ferror) instead.
这是该函数的返回值的原文
成功时,返回读取的字符(提升为 int 值)。
返回类型为 int,以容纳表示失败的特殊值 EOF:(因为EOF其实是一个宏,数值是-1)
如果位置指示器已到达文件末尾,函数返回 EOF 并设置流的 eof 标志(feof)。
如果发生其他读取错误,函数同样返回 EOF,但会设置其错误标志(ferror)。
函数读取到文件末尾会设置eof指示器,但函数怎么知道它读的字符是不是最后一个呢,他只能通过再读一次,看看能不能读到值了,如果到文件末尾,操作系统就会告诉他End-of-File,他才会在流结构体中防止eof指示器
而feof只是看结构体里有没有这个指示器(具体情况可能更复杂,我上述说的只是一个个比喻,大佬勿喷,不对请指出)

浙公网安备 33010602011771号