开发者社区> eddie小英俊> 正文

C语言文件操作解析(五)之EOF解析

简介:
+关注继续查看

C语言文件操作解析(五)之EOF解析

       在C语言中,有个符号大家都应该很熟悉,那就是EOF(End of File),即文件结束符。但是很多时候对这个理解并不是很清楚,导致在写代码的时候经常出错,特别是在判断文件是否到达文件末尾时,常常出错。

1.EOF是什么?

   在VC中查看EOF的定义可知:

   #define EOF     (-1)

   EOF只是代表一个整形常量-1。因此很多人认为在文件的末尾存在这个结束标志EOF,这种观点是错误的。事实上在文件的末尾是不存在这个标志的。那么有人会问那下面的程序如何解释?

    char ch;
while((ch=fgetc(fp))!=EOF)
{
printf("%c\n",ch);
}

   书上都通过这样的代码去判断是否读取到文本文件末尾,就是当读取到EOF的时候就结束操作。这种理解是错误的。

   先看一下函数fgetc的原型:

   int fgetc(FILE *fp);

  事实上在fgetc函数内部,每次都是读取一个字节的数据,而且这一个字节的数据是以unsigned即无符号型处理的,然后将这一个字节的数据赋给一个int型变量作为返回值返回,因此无论从文件中读取的是什么数据,作为无符号型赋值给一个int型变量,返回值不可能是负数。比如当读取到数据0xFA时,因为是以无符号处理的,因此在将0xFA赋值给int型变量的时,int型变量的高位是填充0的(为什么填充0,跟汇编语言里面的符号扩展类似,在后面会提到),因此返回的结果是0X00 00 00 FA,始终不会是负数.而若读取到文件末尾的时候,即没有数据可供读取的时候,那么返回EOF,即-1,这个-1是一个int型常量,二进制表示是0x FF FF FF FF。

    上面的代码具有很大的局限性,因为其只能判断是否到达文本文件的末尾,而不能对二进制文件进行准确的判断。因为正常情况下,文本文件中无论如何是无法读取到-1(0x FF)这个数据,因此可以判断。但是对于二进制文件不同,很可能读到的一个字节的数据就是0xFF,那么返回值此时就是-1,但是此时还未到达文件末尾,造成错误的判断。

    那么有没有办法解决?可以将ch定义为int型即可。

    下面来比较一下下面这段程序和上面程序在执行时的区别。

    int ch;
while((ch=fgetc(fp))!=EOF)
{
printf("%c\n",ch);
}

     假如在文件中读取到的数据是0xFA。

    那么对于上面一段程序的执行过程是:

    将0xFA先赋值给一个int型变量(假如是a),那么此时a为0x 00 00 00 FA,当将返回值a返回给变量ch时,由于ch是char型的,只有8位,那么只将a的低8位赋给ch,那么此时ch为0x FA,而ch是作为有符号处理的,那么此时ch的值肯定是负数。

    而若将ch定义为int型,执行过程为:

    将0xFA先赋值给一个int型变量(假如是a),那么此时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

   feof函数的原型是

   int feof(FILE *fp);

   若到达文件末尾则返回一个非零值,否则返回0。

   在VC中查看feof函数的定义:

   #define _IOEOF          0x0010

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

   可知feof函数判断是否到达文件末尾时与_flag这个标志有关。

   下面看一下这段程序:

复制代码
#include<stdio.h>
#include<stdlib.h>

int main(void)
{
FILE *fp;
int ch;
if((fp=fopen("test.txt","w+"))==NULL)
{
printf("can not open file\n");
exit(0);
}
for(ch=65;ch<=70;ch++)
{
fputc(ch,fp);
}
rewind(fp);
while(feof(fp)==0)
{
ch=fgetc(fp);
printf("%0X\n",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("%0X\n",ch);
ch=fgetc(fp);
}

这样就不会多打印一个FFFFFFFF了。

在上面提到汇编语言中符号扩展的问题,其实在C语言中属于数据类型转换的范畴。下面简要说明一下:

符号扩展只针对将字长小的数据赋给字长大的数据时存在,若是字长大的数据赋给字长小的数据,取低位即可。

下面看一段程序:

复制代码
#include<stdio.h>

int main(void)
{
unsigned char ch1=0XFF;
char ch2=0XFF;
char ch3=0X73;
int a=ch1;
int b=ch2;
int c=ch3;
printf("%d\n%d\n%d\n",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.


本文转载自海 子博客园博客,原文链接:http://www.cnblogs.com/dolphin0520/archive/2011/10/13/2210459.html如需转载自行联系原作者


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
FREEBSD上如何使用c语言和libxml2简单解析XML
由于我们的前台使用C语言编写CGI,如果对方提供XML接口给我们传递数据,就必须有解析的程序,这也可能是今后数据接口的最通用的办法。经过研究,正如使用C语言来生成页面一样,显然使用C语言解析XML要比PHP和ASP要麻烦很多。
1688 0
Android的init过程(二):初始化语言(init.rc)解析
Android的init过程(一) 本文使用的软件版本 Android:4.2.2 Linux内核:3.1.10     在上一篇文章中介绍了init的初始化第一阶段,也就是处理各种属性。
774 0
7.数据本地化CCString,CCArray,CCDictionary,tinyxml2,写入UserDefault.xml文件,操作xml,解析xml
 数据本地化 A CCUserDefault 系统会在默认路径cocos2d-x-2.2.3\projects\Hello\proj.win32\Debug.win32下生成一个名为UserDefault.xml.所有的key皆为char *型,value类型为bool intfloat double std::string. 读操作
1283 0
Java数据导出(写)Excel文件 解析
  在编程中经常需要使用到表格(报表)的处理主要以Excel表格为主。下面给出用java写入数据到excel表格方法:   1.添加jar文件     java导入导出Excel文件要引入jxl.jar包,最关键的是这套API是纯Java的,并不依赖Windows系统,即使运行在Linux下,它同样能够正确的处理Excel文件。
976 0
C# 通过form表单下载文本文件
public void DownLoadConfigFile(string name) { //获取文件字符串内容 var data = _service.
707 0
SAS进阶《深入解析SAS》之开发多语言支持的SAS程序
SAS进阶《深入解析SAS》之开发多语言支持的SAS程序 1. 多语言支持的应用程序是指该程序在世界给第使用时,其能够处理的数据,以及处理数据的方式、信息展现的方式都符合当地的语言、文化习惯,这要求应用程序运行时,能够自动进行与地区、语言相关的处理,也就是通常所说的国际化。 2. SBCS、DBCS、MBCS 单字节字符集SBCS指在该字符集中的字符最多由2个字
1804 0
怎么设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程
10679 0
925
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载