个人觉得很有意思,所以转过来了...
每个人的观点之间都有空行分隔.
-----------------------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------------------------------------------
|