检查 fgetc() 的 EOF 和错误的更好方法是什么?
What is the better way to check EOF and error of fgetc()?
我一直使用这种方法
int c;
while ((c = fgetc(fp))!=EOF)
{
printf("%c", c);
}
因为在我看来它更具可读性和健壮性。但是对于我的回答 , 评论说
if ( feof(fp) ) is more robust than
int c; while ((c = fgetc(fp))!=EOF)
作为
while(1)
{
c = fgetc(fp);
if ( feof(fp) )
{
break ;
}
printf("%c", c);
}
比第一个版本更健壮。那么我应该使用什么版本呢?请解释一下为什么那个版本更好。
编辑
在问题Why is “while ( !feof (file) )” always wrong?中问到为什么控制循环中的feof()总是错误的。但是以正确的方式在 if 条件下检查 feof()
总是错误的?解释是可观的。
我通常这样编写输入循环:
int c;
while (c = fgetc(fp), c != EOF) {
/* do something with c here */
}
/* check if EOF came from an end-of-file or an error */
if (ferror(fp)) {
/* error handling here */
}
您通常不应使用这样的循环条件:
while (!feof(fp)) {
/* do stuff */
}
或
for (;;) {
c = fgetc(fp);
if (feof(fp))
break;
}
因为遇到IO错误时会中断。在这种情况下,fgetc
returns EOF
但未设置文件结束标志。您的代码可能会进入无限循环,因为错误情况通常会持续存在,直到采取外部操作为止。
正确的方法是检查fgetc()
的结果:如果它等于EOF
,你通常可以停止读取更多的数据,因为在IO错误和结束的情况下-文件条件,通常无法读取更多数据。然后您应该检查是否发生错误并采取适当的措施。
建议的改进并没有更好,甚至更不稳健。
如here所述,如果发生读取错误(没有eof),它会进入无限循环。在这种情况下,feof
会 return 0
而 fgetc
return 会 EOF
.
你的版本没有这个问题
此外,您的版本更短、更简单并且非常标准。
2 个有趣的问题
ferror()
ferror()
反映了流 的 错误指示器的状态。当出现罕见的输入错误时设置此标志 并保持设置 直到被清除 - 参见 clearerr()
。如果a read输入错误,后面代码再次读取,没有清除,ferror()
仍然报true,即使后面的read没有错误。
当 fgetc()
returns EOF
时,可能是由于文件结束(常见)或罕见的输入错误。最好检查 feof()
而不是 ferror()
来区分。 ferror()
由于先前的错误而不是当前的情况可能是真的 - 这肯定是文件结束。
int c;
c = fgetc(file);
if (c == EOF) {
if (feof(file)) puts("end-of-file");
else puts("input error");
}
Wide char
:由于 C 中的 出现了测试错误条件的问题。
fgetc()
returns 一个 int
。它的值在 unsigned char
和 EOF
范围内(一些负数)。
int ch;
while ((ch = fgetc(fp)) != EOF) {
// do something with ch
}
if (ferror(fp)) Handle_InputError();
if (feof(fp)) Handle_EndOffFile(); // Usually nothing special
然而 C 允许 unsigned char
的范围比 int
的正数更宽。将 unsigned char
转换为 int
具有实现定义的行为,这可能导致 unsigned char
值被转换为负数 int
- 并且匹配 EOF
.
这样的平台很少见,不属于2015年的主流。大多数都会有UCHAR_MAX <= INT_MAX
,通常使用上面的样式。令人怀疑的是,这些平台是否会变得普遍,因为像上面这样的代码数量依赖于 EOF
不同于 unsigned char
转换为 int
。
如果代码需要处理 UCHAR_MAX > INT_MAX
的罕见情况,那么
int c;
for (;;)
{
c = fgetc(file);
if (c == EOF) {
if (feof(file)) break;
if (ferror(file)) break;
// fall through if both if's fail.
}
// do stuff with c
}
while ( !feof (file) ) always wrong? 中的热门参考强调了代码在检查问题之前使用 fgetc(in)
的结果时经常犯的错误。上面的两个代码在使用 fgetc()
.
的结果之前检查错误条件
第二个代码处理所有情况,包括可能只适用于位于某个早已被遗忘的垃圾堆中的计算机的情况。第一种更为常见。
我一直使用这种方法
int c;
while ((c = fgetc(fp))!=EOF)
{
printf("%c", c);
}
因为在我看来它更具可读性和健壮性。但是对于我的回答
if ( feof(fp) ) is more robust than int c; while ((c = fgetc(fp))!=EOF)
作为
while(1)
{
c = fgetc(fp);
if ( feof(fp) )
{
break ;
}
printf("%c", c);
}
比第一个版本更健壮。那么我应该使用什么版本呢?请解释一下为什么那个版本更好。
编辑
在问题Why is “while ( !feof (file) )” always wrong?中问到为什么控制循环中的feof()总是错误的。但是以正确的方式在 if 条件下检查 feof()
总是错误的?解释是可观的。
我通常这样编写输入循环:
int c;
while (c = fgetc(fp), c != EOF) {
/* do something with c here */
}
/* check if EOF came from an end-of-file or an error */
if (ferror(fp)) {
/* error handling here */
}
您通常不应使用这样的循环条件:
while (!feof(fp)) {
/* do stuff */
}
或
for (;;) {
c = fgetc(fp);
if (feof(fp))
break;
}
因为遇到IO错误时会中断。在这种情况下,fgetc
returns EOF
但未设置文件结束标志。您的代码可能会进入无限循环,因为错误情况通常会持续存在,直到采取外部操作为止。
正确的方法是检查fgetc()
的结果:如果它等于EOF
,你通常可以停止读取更多的数据,因为在IO错误和结束的情况下-文件条件,通常无法读取更多数据。然后您应该检查是否发生错误并采取适当的措施。
建议的改进并没有更好,甚至更不稳健。
如here所述,如果发生读取错误(没有eof),它会进入无限循环。在这种情况下,feof
会 return 0
而 fgetc
return 会 EOF
.
你的版本没有这个问题
此外,您的版本更短、更简单并且非常标准。
2 个有趣的问题
ferror()
ferror()
反映了流 的 错误指示器的状态。当出现罕见的输入错误时设置此标志 并保持设置 直到被清除 - 参见 clearerr()
。如果a read输入错误,后面代码再次读取,没有清除,ferror()
仍然报true,即使后面的read没有错误。
当 fgetc()
returns EOF
时,可能是由于文件结束(常见)或罕见的输入错误。最好检查 feof()
而不是 ferror()
来区分。 ferror()
由于先前的错误而不是当前的情况可能是真的 - 这肯定是文件结束。
int c;
c = fgetc(file);
if (c == EOF) {
if (feof(file)) puts("end-of-file");
else puts("input error");
}
Wide char
:由于 C 中的
fgetc()
returns 一个 int
。它的值在 unsigned char
和 EOF
范围内(一些负数)。
int ch;
while ((ch = fgetc(fp)) != EOF) {
// do something with ch
}
if (ferror(fp)) Handle_InputError();
if (feof(fp)) Handle_EndOffFile(); // Usually nothing special
然而 C 允许 unsigned char
的范围比 int
的正数更宽。将 unsigned char
转换为 int
具有实现定义的行为,这可能导致 unsigned char
值被转换为负数 int
- 并且匹配 EOF
.
这样的平台很少见,不属于2015年的主流。大多数都会有UCHAR_MAX <= INT_MAX
,通常使用上面的样式。令人怀疑的是,这些平台是否会变得普遍,因为像上面这样的代码数量依赖于 EOF
不同于 unsigned char
转换为 int
。
如果代码需要处理 UCHAR_MAX > INT_MAX
的罕见情况,那么
int c;
for (;;)
{
c = fgetc(file);
if (c == EOF) {
if (feof(file)) break;
if (ferror(file)) break;
// fall through if both if's fail.
}
// do stuff with c
}
while ( !feof (file) ) always wrong? 中的热门参考强调了代码在检查问题之前使用 fgetc(in)
的结果时经常犯的错误。上面的两个代码在使用 fgetc()
.
第二个代码处理所有情况,包括可能只适用于位于某个早已被遗忘的垃圾堆中的计算机的情况。第一种更为常见。