8.2 EOF和FEOF的区别

通过上一节的讲解,读者对文件应该有了大概的认识,接下来介绍在文件操作中经常使用到的EOF和FEOF究竟有什么样的区别。

首先,读者需要明白EOF是C语言中的一个宏,相应的实现代码如下:


define EOF(-1)


EOF是end of file的缩写,其值为-1。从字面上也可以知道它以类作为判断文件结束的标志,但是需要注意的是,它只能作为文本文件的结束标志,因为文本文件的存储方式是将字符转换为ASCII码,而ASCII码中不会出现-1。EOF不仅可以用来检测文件是否结束,还可以用来判断函数是否调用成功,下面通过一段代码来看其具体的使用。


include<stdio.h>

void copy(FILEfpin,FILEfpout)

{

char ch;

ch=getc(fpin);

while(!feof(fpin))

{

putc(ch,fpout);

ch=getc(fpin);

}

return;

}

int main()

{

FILEfpin,fpout;

fpin=fopen("text1.txt","r");

fpout=fopen("text2.txt","w");

int c;

while((c=getchar())!=EOF)

{

putchar(c);

c=getchar();

}

copy(fpin,fpout);

fclose(fpin);

fclose(fpout);

return 0;

}


先在当前目录下建立一个text1.txt,在其中输入“hello world!”,由于要测试在按下Ctrl+C组合键之后代码是退出循环执行接下来的程序还是直接退出程序,因此在while循环的后面调用了一个copy函数,其功能是将text1.txt中的内容写入text2.txt中。运行代码,由于程序中有一个while循环,因此可以进行输入,只要输入的不是EOF,就会不停地执行while循环体,但是当按下Ctrl+C组合键时窗口就消失了。为了验证是否执行接下来的语句,打开当前目录后发现,目录下多了一个text2.txt,其中的内容和text 1.txt中的完全相同。所以,EOF不仅可以用来判断文本文件是否结束,还可以用来判断函数是否得到成功调用。

但是在二进制文件中,通常不采用EOF来判断是否到达文件的末端,因为二进制文件中会出现-1值,如果没有到达末端就出现了-1值,那么就会出现误判的情况。先来看下面的代码。


include<stdio.h>

void copy(FILEfpin,FILEfpout)

{

char ch;

while((ch=getc(fpin))!=EOF)

{

putc(ch,fpout);

}

return;

}

int main()

{

FILEfpin,fpout;

char b=0x34;

char a=0xff;

fpin=fopen("dat1.dat","wb");

fpout=fopen("dat2.dat","wb");

fputc(b,fpin);

fputc(a,fpin);

fputc(b,fpin);

fclose(fpin);

fpin=fopen("dat1.dat","rb");

copy(fpin,fpout);

fclose(fpin);

fclose(fpout);

return 0;

}


在上面的代码中,先以二进制文件的格式打开dat1.dat,对其进行写操作,写入的3个字符的ASCII码分别是0x34、0xff、0x34。然后再次打开该文件,对其进行读操作,将其中的字符读取到dat2.dat中。运行完程序后打开dat1.dat和dat2.dat,发现它们的内容并不一样,在dat1.dat中是“44”,而在dat2.dat中的是“4”,由此发现0xff被视为结束符,从而判定文件已到末端,不再对后面的数据进行读写操作,所以后面的数据丢失了。但如果适当修改上面的代码,那么同样可以用EOF作为二进制文件结束符的判断标准,修改方法是将函数copy中的“char ch”改为“short ch”,即:


void copy(FILEfpin,FILEfpout)

{

short ch;

while((ch=getc(fpin))!=EOF)

{

putc(ch,fpout);

}

return;

}


修改为以上代码之后再来看运行结果,这时发现能够完整地将dat1.dat中的数据写入到dat2.dat中去了。下面来分析其实现的原因,由于定义的是short型,占用2字节,读取的0xff实际变为0x00ff,这个值不再是-1(0xffff),所以可以得到完整的数据信息。

通常,对于二进制文件的操作都是采用feof宏来判断的。feof宏是一个带参数的宏,接下来介绍feof宏的实现。


define_IOEOF 0x0010

define feof(_stream)((_stream)->_flag&_IOEOF)


需要注意的是,文件指针的位置是由其中的结构体成员变量_ptr来标识的,只有文件指针已经到达文件的末端再进行读写操作时,其中的文件状态标识符才会被置为_IOEOF,这个时候再调用FEOF宏才会得到其返回值16,表示到了文件的末端。看下面的代码。


include<stdio.h>

int main()

{

FILEfpin,fpout;

char b=0x34;

fpin=fopen("dat1.dat","wb");

fputc(b,fpin);

fclose(fpin);

fpin=fopen("dat1.dat","rb");

char c;

while(!feof(fpin))

{

c=getc(fpin);

printf("%X\n",c);

}

fclose(fpin);

return 0;

}


运行结果:


34

FFFFFFFF


可以发现上面的运行结果中多了一个FFFFFFFF,正如前面介绍的,由于在读到最后一个字符时再次对文件进行读写操作,其中的文件状态标识符才会被置为_IOEOF,因此多输出了一个FFFFFFFF。适当修改上面的代码来去除FFFFFFFF,如下:


include<stdio.h>

int main()

{

FILEfpin,fpout;

char b=0x34;

fpin=fopen("dat1.dat","wb");

fputc(b,fpin);

fclose(fpin);

fpin=fopen("dat1.dat","rb");

char c;

c=getc(fpin);

while(!feof(fpin))

{

printf("%X\n",c);

c=getc(fpin);

}

fclose(fpin);

return 0;

}


运行结果:


34


所以,在使用FEOF的时候要尤其注意这点,否则就会在对文件进行读写操作时得到一些不需要的数据。

需要注意的是,FEOF不仅可以用来判断二进制文件是否结束,还可以用来判断文本文件是否结束。如果遇到文件结束符,那么FEOF的返回值为16,否则为0。可以通过下面的代码来测试。


include<stdio.h>

void main()

{

FILE*fp;

fp=fopen("text1.txt","r");

if(NULL==fp)

{

printf("打开失败!\n");

return;

}

char ch;

printf("feof作为文本文件结束的判断标志\n");

ch=getc(fp);

while(!feof(fp))

{

ch=getc(fp);

printf("%d\n",feof(fp));

}

fclose(fp);

fp=fopen("text2.txt","rb");

if(NULL==fp)

{

printf("打开失败!\n");

return;

}

printf("feof作为二进制文件结束的判断标志\n");

ch=getc(fp);

while(!feof(fp))

{

ch=getc(fp);

printf("%d\n",feof(fp));

}

fclose(fp);

return;

}


运行结果:


feof作为文本文件结束的判断标志

0

0

16

feof作为二进制文件结束的判断标志

0

0

16


在上面的代码中,将FEOF作为文本文件和二进制文件结束的判断标志。首先在当前的路径下建立两个文件text1.txt和text.txt,在两个文件中随机输入3个字符。分析运行结果,在没有到达文件末端的时候,文本文件和二进制文件都打印出0,在到达文件末端时打印出16,由此可知,使用FEOF宏可以作为二进制文件和文本文件结束的判断标志,没有结束时返回值为0,结束后返回值为16。