一个未签名的字符是否可能等于EOF?

10

使用fgetc函数读取流的下一个字符时,通常要检查是否已经到达文件结尾,方法是:

if ((c = fgetc (stream)) != EOF)

其中cint类型。然后,要么已到达文件末尾并且条件失败,要么c将是转换为intunsigned char,预计它与EOF不同—因为确保EOF为负。好的...显然。

但还有一个小问题...通常char类型最多只有8位,而int必须至少有16位,因此每个unsigned char都可以表示为int。尽管如此,在情况下char有16或32位(我知道,实际上从未发生过...),没有理由不能使sizeof(int)==1,以便(理论上! )可能fgetc(stream)返回EOF(或另一个负值),但文件末尾尚未到达...

我错了吗? C标准中是否有防止fgetc在未到达文件末尾时返回EOF的内容? (如果是,则我找不到它!)。还是if ((c = fgetc(stream)!= EOF)语法不完全可移植?...

编辑:确实,这是问题#3860943的重复。我一开始没有找到那个问题。谢谢你的帮助! :-)


我已经阅读过了,但这并没有回答我的问题... - Rémi Peyre
4个回答

2

您的问题是:

fgetc函数在未到达文件结尾时是否会返回EOF是由C标准所限制的吗?

相反,标准明确允许在发生错误时返回EOF

如果出现读取错误,则设置流的错误指示器,并使fgetc函数返回EOF

在注释中,我看到:

可以通过使用feofferror函数来区分文件结尾和读取错误。

您还问:

if ((c = fgetc (stream)) != EOF)语法是否完全可移植?

在理论上的平台上,如果CHAR_BIT大于8且sizeof(int)==1,那么这不是一种有效的检查文件结尾的方法。为此,您必须使用feofferror

c = fgetc (stream);
if ( !feof(stream) && !ferror(stream) )
{
  // Got valid input in c.
}

1
sizeof(int) == 1 时,我喜欢使用 if ( !feof(stream) && !ferror(stream) ) 测试。为一个可移植的通用解决方案加一分。 - chux - Reinstate Monica

2
如果你正在读取的流只包含标准ASCII字符,那么在真正的文件结尾之前,不会收到等效于EOF的字符,因为有效的ASCII字符代码只有127。但是,在读取二进制文件时可能会发生这种情况。字节需要是255(无符号),才能对应于-1有符号字符,并且没有任何东西阻止它出现在二进制文件中。
但是针对你所提出的具体问题(是否有标准规范),并没有确切的答案。请注意,fgetc将字符作为无符号字符进行推广,因此在这种情况下,它永远不会是负数。唯一的风险是如果你已经显式或隐式地将返回值向下转换为signed char(例如,如果你的c变量是signed char)。
注意:正如@Ulfalizer在评论中提到的那样,有一种罕见的情况可能需要担心:如果sizeof(int)==1,并且你正在读取包含非ASCII字符的文件,则可能会得到一个不是真正EOF的-1返回值。请注意,发生这种情况的环境非常少见(据我所知,是针对低端8位微控制器(例如8051)的编译器)。在这种情况下,安全的选择是按照@pmg建议的方式测试feof()。

请注意,例如测试0xFFFFFFFF == -1对于32位的int是成立的。通常的算术转换将-1转换为unsigned int - Ulfalizer
@Ulfalizer,我的意思是相反的。如果fgetc找到一个0xFF字节来读取,它会提升为0x000000FF(因此是正255),而不是0xFFFFFFFF,因为它被提升为unsigned char。在这里看到fgetc实现的示例:http://mirror.fsf.org/pmon2000/3.x/src/lib/libc/fgetc.c - Fabio Ceconello
但是C语言并不仅限于ASCII字符集。 - P.P
如果charint的大小相同,那么你可能会得到一个0xFFFFFFFF的char值。我猜标准在一些地方可能会暗示该值应该表示为有符号的int。无论如何,从无符号转换为有符号是未定义的行为。 - Ulfalizer
当值无法适应有符号类型时。 - Ulfalizer
根据我的理解,标准规定只有当int和char的大小都为1时,它们才相等。Int可能会更大,但char不会。请参考https://dev59.com/tXE95IYBdhLWcg3wp_qn,但在那些罕见的情况下,如果sizeof(int)== 1,你是正确的,因为一个255的无符号char将会转换为一个-1的1字节int。 - Fabio Ceconello

2

我认为您需要依赖于流错误。

ch = fgetc(stream);
if (ferror(stream) && (ch == EOF)) /* end of file */;

来自标准

如果发生读取错误,则会设置流的错误指示器,并且fgetc函数返回EOF。


为更好的版本进行编辑

ch = fgetc(stream);
if (ch == EOF) {
    if (ferror(stream)) /* error reading */;
    else if (feof(stream)) /* end of file */;
    else /* read valid character with value equal to EOF */;
}

3
我认为到达文件结尾并不是读取错误。 - Diego
1
也许不用...总有 feof() - pmg
问题实际上是:如果未达到文件末尾且没有读取错误,实现中 ch == EOF 为 true 是否允许? - ouah

1

我同意你的阅读。

C标准规定(C11,7.21.7.1 The fgetc function p3):

如果流的文件结束指示器被设置或者流已经到达文件结尾,则将该流的文件结束指示器设置为EOF并且fgetc函数返回EOF。否则,fgetc函数将返回指向流的下一个字符。如果发生读取错误,则设置流的错误指示器并且fgetc函数返回EOF。

在标准中没有任何规定(假设>),禁止托管实现中的fgetc返回等于EOF的值,既不是文件结束指示器也不是错误条件指示器。


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