使用fgetc时,是否可以将EOF与正常的字节值混淆?

时间:2015-09-17 23:11:19

标签: c language-lawyer binaryfiles fgetc

我们经常使用fgetc这样:

int c;
while ((c = fgetc(file)) != EOF)
{
    // do stuff
}

理论上,如果文件中的某个字节的值为EOF,则此代码存在错误 - 它会提前中断循环并且无法处理整个文件。这种情况可能吗?

据我了解,fgetc在内部将从文件读取的字节转换为unsigned char,然后转换为int,并将其返回。如果int的范围大于unsigned char的范围,则此方法有效。

如果不是(可能那么sizeof(int)=1)会发生什么?

  • 有时会fgetc从文件中读取等于EOF的合法数据吗?
  • 它会改变从文件中读取的数据,以避免单值EOF
  • fgetc会成为未实现的功能吗?
  • EOF会是long的其他类型吗?

我可以通过额外的检查使我的代码变得简单:

int c;
for (;;)
{
    c = fgetc(file);
    if (feof(file))
        break;
    // do stuff
}

如果我想要最大程度的便携性,这是必要的吗?

3 个答案:

答案 0 :(得分:5)

C规范规定int必须至少能够保存-32767到32767之间的值。任何int较小的平台都是非标准的。

C规范还说EOF是一个负int常量,fgetc在事件中返回“unsigned char转换为int”成功阅读。由于unsigned char不能包含负值,因此可以将EOF的值与从流中读取的任何内容区分开来。 *

* 请参阅下文,了解未能解决的漏洞案例。

相关标准文本(来自C99):

  • §5.2.4.2.1整数类型的大小<limits.h>

      

    [实现 - 定义的值的大小(绝对值)应等于或大于显示的值,并带有相同的符号。

         

    [...]

         
        
    • int类型对象的最小值      
          

      INT_MIN -32767

        
    •   
    • int类型对象的最大值      
          

      INT_MAX +32767

        
    •   
  • §7.19.1<stdio.h> - 简介

      

    EOF ...扩展为整数常量表达式,类型为int,负值,由多个函数返回以指示文件结束,即不再输入来自流

  • §7.19.7.1fgets函数

      

    如果stream指向的输入流的文件结束指示符未设置且存在下一个字符,则fgetc函数将该字符作为unsigned char获取转换为int并推进流的关联文件位置指示符(如果已定义)

如果UCHAR_MAXINT_MAX,则没有问题:所有unsigned char值都将转换为非负整数,因此它们将与EOF区别开来。

现在, 是一个有趣的漏洞:如果系统有UCHAR_MAX&gt; INT_MAX,然后法律允许系统将大于INT_MAX的值转换为负整数(根据§6.3.1.3,将值转换为无法表示该值的有符号类型的结果为实现定义),使从流中读取的字符可以转换为EOF。

确实存在CHAR_BIT > 8的系统(例如显然使用32位字节的TI C4x DSP),尽管我不确定它们是否在EOF和流功能方面被打破。

答案 1 :(得分:4)

是的,c = fgetc(file); if (feof(file))确实可以实现最大的可移植性。它通常起作用,当unsigned charint具有相同数量的唯一值时也是如此。这种情况发生在使用charsigned charunsigned charshortunsigned shortintunsigned的罕见平台上相同的位宽和范围宽度。

请注意feof(file))不足。代码还应检查ferror(file)

int c;
for (;;)
{
    c = fgetc(file);
    if (c == EOF) {
      if (feof(file)) break;
      if (ferror(file)) break;
    }
    // do stuff
}

答案 2 :(得分:0)

注意:在最常见的情况下,chux的答案是正确的。我将这个答案留下来,因为我相信评论中的答案和讨论对于理解chux的方法是必要的(罕见)情况是有价值的。

EOF保证具有负值(C99 7.19.1),如您所述,fgetc在转换为int之前将其输入读取为unsigned char。所以那些自己保证不能从文件中读取EOF。

至于你的具体问题:

  • fgetc无法读取等于EOF的合法数据。在文件中,没有签名或未签名的东西;它只是位序列。它是C,以不同的方式解释1000 1111,具体取决于它是被视为有符号还是无符号。 fgetc需要将其视为无符号,因此无法返回负数(EOF除外)。

    附录:它无法读取unsigned char部分的EOF,但是当它将unsigned char转换为int时,如果int不能表示unsigned char的所有值,则行为是实现定义的(6.3.1.3)。

  • fgetc是托管实现的标准所必需的,但是允许独立实现省略大多数标准库函数(有些显然是必需的,但我找不到列表。)

    < / LI>
  • EOF不需要很长时间,因为fgetc需要能够返回它并且fgetc返回一个int。

  • 就改变数据而言,它不能完全改变,但由于fgetc被指定为从文件读取“字符”而不是字符,它可以可能一次读取8位,即使系统另外将CHAR_BIT定义为16(如果sizeof(int)== 1,则它可能具有的最小值,因为INT_MIN&lt; = -32767和INT_MAX&gt; = 32767 5.2.4.2)要求。在这种情况下,输入字符将转换为 unsigned char ,它始终具有高位0.然后它可以转换为int而不会丢失精度。 (实际上,这不会出现,因为机器一般不具有16位字节)