C标准:如果fgets返回非NULL,但已经遇到文件末尾,feof会返回真吗?
while (fgets(s, sizeof(s), f) != NULL) {
if (feof(f)) {
break;
}
}
请考虑以下情形:对 fgets 的调用将一个或多个字符读入到 s,但随后停止读取,恰恰因为遇到了文件结束。C标准规定,在这种情况下,fgets 不会返回空指针,因此循环体将执行。在那次迭代中,按照标准,feof 是否一定会返回true?这大概是个傻问题,但我一直觉得只有当I/O函数的返回值真正指示失败时,才应该调用 feof(以及 ferror)。
解决方案
设想这样的情形:
fgets到达EOF,但确实向s读入了一些内容,
fgets 将最多向 s 读取 sizeof(s)-1 字节。如果它读取的字节数少于该值、且未读取到换行符,但至少读取了1 字节,那么 fgets 将返回 s,并且EOF标记将被设置,这意味着对 feof 的调用将保证返回true。
The fgets 函数被归类为 字节输入函数,按 [C23草案标准] 第7.23.1p6段的定义。第7.23.3p11节进一步指出:
字节输入函数从流中读取字符,仿佛是通过连续调用
fgetc函数
And section 7.23.7p3 regarding the fgetc function states:
如果流的end-of-file指示符被设定,或者流已经到达end-of-file,则该流的end-of-file指示符被设定,fgetc函数返回EOF。
这意味着如果 fgets 尝试读取超出文件结束的内容,那么EOF指示符将被设定,随后 feof 将返回true。
例如,如果 s 被定义为 char s[5],且文件中还剩下3 个字节要读取,则 s 将包含这3 个字节,后跟一个空字节(null字节),并且EOF指示符将被设定。
请注意,读取到文件结束但不越过它并不会设置EOF标志。因此,在上面的例子中,如果文件中还剩下4 个字节要读取,或者少于4 个但最后一个字节是换行符,则不会设置EOF;对 fgets 的下一次调用将返回 NULL,并设置EOF标记。