返回列表 发帖

[转帖]csdn一个关于文件结尾判断的讨论

个人觉得很有意思,所以转过来了... 每个人的观点之间都有空行分隔. -----------------------START--------------------------------------------------- ------------------------------------------------------------------------------- 我写了个文件访问的程序,二进制读,用feof()判断文件结尾,结果发现它总是把EOF(0xFF)当做最后的字节读出。   比如:我做一个100个字节的文件,它读出前100个正确,然后把0xFF作为最后一个字节也读出来;   我索性存盘一个空文件(0 byte),结果它也能读回一个字节0xFF,晕~~   我使用的格式为   while(!feof(fp))   {   cc=fgetc(fp);   ....   } ------------------------------------------------------------------------------- 本就是如此,如果你这段代码是抄书来的,赶快丢掉那本书(谭浩强的书上有类似代码,那是一本可以丢掉的书)。 另外,定义你的cc为int,然后使用如下代码 while ( (cc = fgetc(fp)) != EOF) { .... }; ------------------------------------------------------------------------------- 老兄,我可是用二进制方式打开文件的,(cc = fgetc(fp)) != EOF ,如果在文件结尾之前就有0xFF出现,这不就退出循环了吗? ------------------------------------------------------------------------------- 谢谢两位兄弟,我已经理解了,修改成功:) 给二位各加10分,呵呵 wanguodu(足文字D),你的理解也不够正确, 关键是cc要定义成int型的,如果是char,遇到0xFF就跳出。 ------------------------------------------------------------------------------- 正好你在,我觉得你说: 请注意函数原型: int fgetc(FILE *); 不够准确。 必须把cc定义成int,才可以, 我试了cc定义成char,遇到0xFF跳出了。 因为int fgetc(FILE *);读出的值是0x00FF,但给cc时成了0xFF,也就是-1, 而EOF不管在多少位机上都是全F,EOF也是-1,哈哈 ------------------------------------------------------------------------------- 最后读出来的那个-1不是字符,fgetc的返回值是读取遇到错误的时候就返回-1。打开文件,读完最后一个字符,文件内部指针就指向最后一个字符的下一个位置了,再读一次的话,fgetc就认为读取错误,因此返回-1,同时由于是文件末尾,fp里面的flags成员的_F_EOF被置1,因此feof()检测出EOF,就退出循环。 由于fgetc的返回值是int,因此那个-1是0xffff,不是0xff。如果c是char的话,那么高八位会被截取,低八位送c。 ------------------------------------------------------------------------------- 还有,fgetc那个返回-1的值严格来说不应该称为EOF,因为EOF意思是文件末尾,而fgetc操作过程中如果遇到错误即使不是文件末尾也一样返回-1,因此这个-1返回值并不是EOF的意思,只不过值跟EOF恰好一样罢了。 ------------------------------------------------------------------------------- 因为int fgetc(FILE *);读出的值是0x00FF,但给cc时成了0xFF,也就是-1。 ~~~~~~~~~~~~~~~~~ ~~~~~~~~~ 尽管交了朋友,我还是要纠正你一下: 1、0xFF只有在8位机上才是-1。在16位机上,-1是0xFFFF, 在32位机上是0xFFFFFFFF; 2、函数fgetc()在读到文件尾部或出错时都是返回EOF。在VC和gcc中,EOF==-1, 而不是0x00FF, 只不过因为你将cc定义为了char,返回时-1(16位机上为0xFFFF)被截短为0xFF给了cc。 。——这个人天生爱较真,请这位朋友不要介意; ------------------------------------------------------------------------------- 楼上的,用feof是可以的。不过,由于feof的语义是“当第一读过文件尾时返回非0值,否则返回0”,也就是说,在读到文件最后一个字符之后feof也返回0,只有再读一次之后调用feof才返回非0值。楼主的程序如果改成: int cc; while( ! feof(fp))   {    cc = fgetc(fp); if(cc == EOF) { break; }    ....   } 就对了。可以发现,其实feof在这里完全是多余的,更好的实现就是antijpn(antijpn) 给出的: int cc; while( (cc=fgetc(fp)) != EOF ) { .... } 不过,feof可以用在fread的场合,因为fread在读过文件尾时不会返回EOF,而是返回实际读到的完整item个数(可能少于要读的item个数),下面的例子来自MSDN: Example /* FEOF.C: This program uses feof to indicate when * it reaches the end of the file FEOF.C. It also * checks for errors with ferror. */ #include #include void main( void ) { int count, total = 0; char buffer[100]; FILE *stream; if( (stream = fopen( "feof.c", "r" )) == NULL ) exit( 1 ); /* Cycle until end of file reached: */ while( !feof( stream ) ) { /* Attempt to read in 10 bytes: */ count = fread( buffer, sizeof( char ), 100, stream ); if( ferror( stream ) ) { perror( "Read error" ); break; } /* Total up actual bytes read */ total += count; } printf( "Number of bytes read = %d\n", total ); fclose( stream ); } Output Number of bytes read = 745 -------------------------------------------------------------------------------- 你错了,最好的用法是feof(),而不是cc=fgetc(fp)) != EOF ,读取错误也会产生-1,但并不意味着文件末尾,而feof()是检测flags成员中的_F_EOF位,_F_EOF位只有到了真正的文件末尾,才会被置1。用cc=fgetc(fp)) != EOF并不能正确判断是否真的到了文件末尾。 -------------------------------------------------------------------------------- 1、谢谢楼上的补充,改成下面那样就更完善了: while ( (cc = fgetc(fp)) != EOF) { .... } if(feof(fp)) { .... } if(ferror(fp)) { .... } 2、fgetc读取错误也会产生-1 ~~~~~~~~~~~~ 准确地说是返回EOF, 呵呵。 3、你所说的feof的处理方式不过是一种实现罢了,也许很多主流的C库都是这么实现的,但这并不是必须的。应该是先有接口定义,再有实现。而实现可以是多种多样的,只要不改变接口的语义就可以了。当然你给出的这个实现对于理解feof的语义是很有帮助的。 -------------------------------------------------------------------------------- 无论是*BSD的man还是MSDN,都说了fgetc遇到错误/文件结束都返回EOF,并且用feof和ferror来区分,没有谁规定了EOF一定是-1,直接写-1是一种对移植有百害而无一利的事情。 标准的要求是EOF只要是一个负整数就可以了,对于大多数实现来说,这个是1 -------------------------------------------------------------------------------- 2、函数fgetc()在读到文件尾部或出错时都是返回EOF。在VC和gcc中,EOF==-1, 而不是0x00FF, 只不过因为你将cc定义为了char,返回时-1(16位机上为0xFFFF)被截短为0xFF给了cc。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 你没有理解我的话, “我试了cc定义成char,遇到0xFF跳出了。 因为int fgetc(FILE *);读出的值是0x00FF,但给cc时成了0xFF,也就是-1,” 当(二进制)文件中出现0xFF时,fgetc()读出的值是0x00FF,没问题吧? 可cc是char型,于是cc=0xFF; cc和EOF比较,当然相等了。 程序跳出... -------------------------------------------------------------------------------- -1 = 0xFF?!! 我已经说过了, 在16位机上-1=0xFFFF, 在32位机上-1=0xFFFFFFFF.难道你的是8位机? -------------------------------------------------------------------------------- 这个问题其实跟用什么来代表EOF没有任何关系。cc = fgetc(fp)) != EOF的漏洞在于,即使返回一个EOF也并不能说明到了文件末尾,只有feof()才能真正说明到了文件末尾。 -------------------------------------------------------------------------------- 唉,我晕倒! 我的话关键在char 还是int 上,你却老扯16位机32位机。 如果把cc定义成char型,有无unsigned好像都一样,我试验过了,读出的数据中碰到0xFF,就不往后读了,认为到了文件结尾。 -------------------------------------------------------------------------------- 站长是不是有点火了? 抱歉. 你说得没错, 关键是要把cc定义成int型. 如果cc定义成char型, 则当读到一个0xFF时, 循环确实会退出, 即使还没读到文件尾. 我和你的分歧主要在于cc定义为char型时循环意外退出的原因上, 按你的意思: "因为int fgetc(FILE *);读出的值是0x00FF,但给cc时成了0xFF,也就是-1". 而我的意思是, 0xFF与-1(VC6上是0xffffffff)不是等价的. 真实的原因是, 当char型的cc与-1比较的时候, 按ANSI C的规定, 编译器是在将cc符号扩展为int型之后再于-1比较的, 当然相等了. 事实上, 按ANSI C规定, 在进行算术/逻辑运算时, 对于char, short, int型的变量都是类型提升到int(包括符号扩展)之后再进行的. 另外, 如果把cc定义成unsigned char型的话, 结果应该与定义为char型不一样, 因为这时cc在与-1比较的时候虽有类型提升但没有符号扩展, 这样的话在VC6上, unsigned char型的0xFF类型提升后变为0x000000FF, 与-1即0xFFFFFFFF比较当然不等了. -------------------------------------------------------------------------------- 我试验过了: int时候0XFF是-1,而 char时候0XFFFF是-1!~ 就这样,不要吵了~~! -------------------------------------------------------------------------------- -----------------------------------END------------------------------------------

返回列表 回复 发帖