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