zoukankan      html  css  js  c++  java
  • 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.

  • 相关阅读:
    MQTT TLS 加密传输
    python多进程并发redis
    各种消息队列的特点
    mqtt异步publish方法
    Numpy API Analysis
    Karma install steps for unit test of Angular JS app
    reinstall bower command
    Simulate getter in JavaScript by valueOf and toString method
    How to: Raise and Consume Events
    获取对象的类型信息 (JavaScript)
  • 原文地址:https://www.cnblogs.com/dolphin0520/p/2210459.html
Copyright © 2011-2022 走看看