ASCII码0x1A使文件意外结束的处理方法

       在windows操作系统中,大家在编写带有文件操作的程序时,有时候会遇到一种奇怪的现象,在对一个文件以文本方式进行读取的过程中,读到中途还没到文件末尾时feof()函数就意外地为真,这让人很惊讶,一时难以找到原因,实际上,这是ASCII码0x1A在作怪。运行下面程序:

int main(void)
{
        int i;
        unsigned char c;
        FILE *fp;        
        fp=fopen("test.dat", "w");
        fprintf(fp, "abc/x1A def");
        fclose(fp);
        fp=fopen("test.dat", "r");
        for(i=0; i<=7; ++i)
        {
                fread(&c, sizeof(char), 1, fp);
                printf("%02X feof=%d/n", c, feof(fp));
        }
        fclose(fp);
        return 0;
}

运行的结果是:

61 feof=0
62 feof=0
63 feof=0
63 feof=16
63 feof=16
63 feof=16
63 feof=16
63 feof=16

        从以上结果可见,在读到第四个字符就是0x1A的时候,feof为真了。这种现象目前只发现存在于windows中,unix/linux没有。为什么会这样呢?

        0x1A在ASCII码中代表EOF,在过去,ASCII码EOF曾经在unix/linux中被作为文件结束符使用,微软继承了这个传统,也以EOF作为文件的结束符,不过,笔者手里的一些资料表明,微软在dos5.0以后就抛弃了这种做法。但实际情况是,笔者在dos6.22、windows3.1、windows3.2、windows9x、windows2k、xp、2003都存在这种问题。同时,这种问题是系统还是库函数造成的也有待进一步查证,由于没有源码,无法证实,如果哪位朋友有这方面的资料,希望可以共享。另一方面,鉴于dos/windows下所有主流编译器例如VC、BCB、gcc、tc2.0、bc3.1等都是同样的结果,笔者倾向于这是系统原因造成的。

        那么这种0x1A现象是否符合标准呢?C89/C99是这样定义文件流的两种方式的:

7.19.2 Streams

    A text stream is an ordered sequence of characters composed into lines, each line consisting of zero or more characters plus a terminating new-line character. Whether the last line requires a terminating new-line character is implementation-defined. Characters may have to be added, altered, or deleted on input and output to conform to differing conventions for representing text in the host environment.  Thus, there need not be a one-to-one correspondence between the characters in a stream and those in the external representation.

    A binary stream is an ordered sequence of characters that can transparently record internal data. Data read in from a binary stream shall compare equal to the data that were earlier written out to that stream, under the same implementation. 

        规定中说明,文本方式可以对输入输出文件的字符进行添加、替换和删除,0x1A现象是否属于这三种情况之列呢?笔者认为不属于,因为它在逻辑上使文件发生了截断,已经不仅仅是对字符的添加、替换或删除了。如果这被证实是非法的话,那么,无论是库函数还是系统的原因,库函数都有责任对此进行修正。

        如何解决这个问题?其实标准已经给出了其中一种解决方法,由于二进制方式的输入输出是一一对应的,字符不会产生变化,那么用二进制读取就不会产生这个问题,事实也证明是可以的,只是存在一点麻烦,由于windows下会把/n转换为/r/n,二进制读取时必须对此转换进行堙别和还原,这会增加代码的复杂性,这种麻烦很让人讨厌。因此笔者尝试在低级函数中寻找更好的答案,但让人失望的是几款主流编译器的低级函数即使在文本方式下都没有对/r/n进行转换,由于低级函数的行为跟标准无关,如果有哪款编译器的低级函数对/r/n进行了还原,那就是比二进制方式更好的解决方法。

        但无论如何,以上方法都不完美,这是否意味着windows的文本方式没有任何意义?这实在让人沮丧。如果哪位朋友对此有深入的研究,欢迎一起讨论。

其实这是一个连初学者都不应该犯的错误,文本方式只能读取文本文件,也就是不包含控制字符(换行除外)的文件,ASCII值小于0x20的都是控制字符。 

文本方式 和 二进制方式 的不同在于。 
1. 文本方式只能读取不包括控制字符(换行除外)的文件 
2. 文本方式会对换行执行翻译。比如windows下,把文件中的/r/n翻译为/n,把写入文件的/n翻译为/r/n;unix下,把文件中的/n翻译为/n,把写入文件的/n翻译为/n。
# megaboy 发表于2005-11-21 19:42:00  IP: 218.19.57.*
“文本方式和二进制方式唯一的区别就是对命令字符执行命令解释”。 
------------------------------------------------------------- 
你都没分清楚什么是标准,什么是实现。如果某个编译器把控制字符解释为执行命令,这叫实现相关。而标准是这么说的: 


Characters may have to be added, altered, or deleted on input and output to conform to differing conventions for representing text in the host environment. Thus, there need not be a one-to-one correspondence between the characters in a stream and those in the external representation. 
# megaboy 发表于2005-11-21 12:57:00  IP: 219.137.11.*
楼上如果没有见过标准,先去看看再说,OK?标准并没有把文本方式限制在可打印字符之内。
# zjneter 发表于2005-11-21 08:36:00  IP: 221.195.41.*
兄弟你发现的这个问题很好,看似个小问题,实际上是非常重要的。不如把你发现的问题提交到标准委员会,让那些大牛们出个主意 :)
# moonlight 发表于2005-11-22 09:20:00  IP: 10.40.52.163, 210.51.195.*
事实上,所谓的控制字符也只对文本文件才有意义,二进制文件中的所有字符都看作透明流,无须任何控制字符。
# Oliver 发表于2005-12-08 10:57:00  IP: 210.72.232.*
我也刚刚遇到这个问题。多谢楼主提出讨论。在打开的时候用stream = fopen( m_FileName, "rb");,以2进制方式打开文件就可以解决这个问题了。
# Mike 发表于2006-05-29 20:29:00  IP: 210.22.79.*
遇到过这个问题,搞得头很痛。 
1、用文本方式打开二进制文件进行拷贝时,没到文件结束时,就结束了。 
2、用文本方式打开文本文件进行拷贝时,没问题。 
3、用二进制方式打开文本文件或二进制文件进行拷贝,都没有问题。 
目前唯一保险的作法就是,只涉及到文件拷贝时,都以二进制方式打开,按字节拷贝。 


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值