作者:海子来源: http://www.cn blogs.com/dolphin 0520 /
c语言文件操作分析(五)的EOF分析
在c语言中,有一个应该是众所周知的符号。 那是eof (结束文件),是文件结束符。 但是,这种理解往往不明确,在编写代码时经常发生错误,特别是在确定文件是否到达文件末尾时。
1 .什么是Eof?
用VC看EOF的定义是:
#定义Eof (-1 ) ) )。
EOF只是表示整形常数-1。 因此,很多人认为在文件的末尾存在该结束标志EOF,但这种想法是错误的。 其实在文件的末尾不存在这个标记。 那么,下一步怎么解释?
char ch; wile((ch=fgetc ) FP )!=EOF ) printf('%cn ',ch ); }
在本例中,用这样的代码判断是否读取到文档的末尾是在读取到EOF时结束操作。 这个理解是错误的。
/*
这一段不是原文的内容。 添加以补充这里的说明
例如,以下文本文件中的磁盘内容在文件末尾没有-1:
*/
让我们先看看函数fgetc的原型:
int fgetc (文件* FP );
实际上,在fgetc函数内部,每次都会读取1字节的数据。 然后,以unsigned或无符号类型处理该1字节数据,并将该1字节数据分配给一个int类型变量,并作为返回值返回。 因此,无论从文件读取什么数据,都将一个int类型变量指定为无符号类型,返回值不是负数。 例如,如果读取的是数据0xFA,则处理时没有符号,因此将0xFA代入int类型变量时,int类型变量的高位将被0填充(为什么填充0与程序集语言中的符号扩展类似)。 后述)。 其结果为0X00 00 00 FA,并不总是负数。 读取到文件末尾时,将无法读取数据
上面的代码有很大的限制。 因为只能判断是否到达了文本文件的末尾,无法正确判断二进制文件。 通常,文本文件无法读取-1(0xff )数据,因此可以判断。 但是,根据二进制文件,1字节的数据为0xFF的可能性较高,返回值在这个点是-1,但是在这个点还没有到达文件末尾,会做出错误的判断。
那么有解决的方法吗? ch可以定义为int型即可。
比较一下下面的程序和上面的程序运行时的区别。
int ch; wile((ch=fgetc ) FP )!=EOF ) printf('%cn ',ch ); }
假设读取到文件中的数据为0xFA。
上一个程序的执行过程是:
如果先将0xFA代入1个int型变量中,则此时a为0x 00 00 00 FA。 如果将返回值a返回变量ch,则ch为char型,只有8位,因此如果只将a的低位8位代入ch,则此时ch为0xFA,ch被视为带符号。 此时,ch的值一定为负数。
如果将ch定义为int类型,则运行进程为:
如果先将0xFA代入一个int型变量,则此时a为0x 00 00 00 FA,如果将返回值a返回变量ch,则ch也为int型,因此ch与0x 00 00 00 FA为正数,执行两个程序得到的结果完全不同。
看看读取的数据为0x FF (此时未到达文件末尾)时会产生什么样的结果。
在ch为char型情况下,在返回返回值0x 00 00 00 FF时,若将低位8比特分配给ch,则此时ch为-1,误判定为到达了文件末尾;
另一方面,ch为int型时,如果返回返回值0x 00 00 00 FF,则ch的值为0x 00 00 00 FF,ch不是-1,不会误判定为文件末尾。
(当然,上述内容必须在读取无误的情况下成立)
所以很多时候会使用函数feof。
二. feof
EOF函数的原型是
int feof (文件* FP );
如果到达文件末尾,则返回非零值,否则返回0。
用VC看feof函数的定义:
#define _IOEOF0x0010
#definefeof(_stream ) ) _stream )-_flag _IOEOF () ) ) ) ) ) ) )。
可知判断feof函数是否到达文件末尾与名为_flag的标志有关。
看看这个程序:
#include#includeintmain(void ) { FILE *fp; int ch; if () FP=fopen('test.txt ',' w ' ) )==NULL ) (printf ) ) cannotopenfilen ); 退出(0;
} for(ch=65;ch<=70;ch++) { fputc(ch,fp); } rewind(fp); while(feof(fp)==0) { ch=fgetc(fp); printf("%0Xn",ch); } fclose(fp); return 0;}执行结果是:
41 42 43 44 45 46 FFFFFFFF Press any key to continue
为什么最后打印结果会多打印一个FFFFFFFF?不是只往文件中写入了数据65-70么?
先看一下C++ Reference中关于feof函数的描述(C++ Reference是一个比较好的网站,里面是关于C++所有库函数的描述,网址在博客首页的链接中有,http://www.cplusplus.com/reference/):
Checks whether the End-of-File indicator associated with stream is set, returning a value different from zero if it is. This indicator is generally set by a previous operation on the stream that reached the End-of-File.
从描述中可知,只有当与文件关联的流到达文件末尾时,此时若再进行读取操作,文件结束的标志(上面所述的_flag)才会被重新置位。
因此在上述程序中,当读取完最后一个字节的数据后,文件结束标志并没有被置位,只有当位置指针到达末尾时,再发生读取操作时,而此时又没有数据可供读取,因此返回-1,所以打印出的结果中会多一个FFFFFFFF,在这之后才会将_flag重新置位,此时feof函数才能检测出已经到达了文件末尾。
那么可以通过下面的办法解决这个问题:
ch=fgetc(fp); while(feof(fp)==0) { printf("%0Xn",ch); ch=fgetc(fp); }
这样就不会多打印一个FFFFFFFF了。
在上面提到汇编语言中符号扩展的问题,其实在C语言中属于数据类型转换的范畴。下面简要说明一下:
符号扩展只针对将字长小的数据赋给字长大的数据时存在,若是字长大的数据赋给字长小的数据,取低位即可。
下面看一段程序:
#includeint main(void){ unsigned char ch1=0XFF; char ch2=0XFF; char ch3=0X73; int a=ch1; int b=ch2; int c=ch3; printf("%dn%dn%dn",a,b,c); return 0;}
执行结果为:
255
-1
115
原因是由于ch1、ch2、ch3都是char型变量,只占一个字节,区别在于ch1是无符号的,在将ch1赋值给a时,ch1是看做无符号数据进行处理的,那么在填充a的高位是用0去填充;而对于ch2和ch3都是有符号的,那么在填充高位时就要注意了,若ch2的最高位为0,那么表示ch2是正数,此时填充高位用0填充,而若ch2的最高位为1,则填充高位数据用1填充。
如程序执行的结果所示,由于ch2的最高位为1,那么在填充b的高位的时候会用1去填充,那么b为0X FF FF FF FF;而ch3的最高位为0,那么填充c的高位用0填充,所以c的值为0x 00 00 00 73.