程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C語言文件操作解析(五)

C語言文件操作解析(五)

編輯:關於C語言

在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. www.2cto.com

二.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.

 作者:海 子

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved