1. 程式人生 > 其它 >c語言怎麼讓兩個函式同時進行_C語言處理I/O的兩個隨機訪問函式:fseek()和ftell()...

c語言怎麼讓兩個函式同時進行_C語言處理I/O的兩個隨機訪問函式:fseek()和ftell()...

技術標籤:c語言怎麼讓兩個函式同時進行

有了fseek()函式,便可把檔案看作是陣列,在fopen()開啟的檔案中直接移動到任意位元組處。我們建立一個程式reverse.c 演示fseek()和ftell()的用法。注意,fseek()有3個引數,返回int型別的值;ftell()函式返回一個long型別的值,表示檔案中的當前位置。

/* reverse.c -- displays a file in reverse order */#include #include #define CNTL_Z '032'   /* eof marker in DOS text files */#define SLEN 81int main(void){    char file[SLEN];    char ch;    FILE *fp;    long count, last;    puts("Enter the name of the file to be processed:");    scanf("%80s", file);    if ((fp = fopen(file,"rb")) == NULL)    {                               /* read-only mode   */        printf("reverse can't open %sn", file);        exit(EXIT_FAILURE);    }    fseek(fp, 0L, SEEK_END);        /* go to end of file */    last = ftell(fp);    for (count = 1L; count <= last; count++)    {        fseek(fp, -count, SEEK_END); /* go backward      */        ch = getc(fp);        if (ch != CNTL_Z && ch != 'r')  /* MS-DOS files */            putchar(ch);    }    putchar('n');    fclose(fp);    return 0;}

下面是對一個檔案的輸出:

Enter the name of the file to be processed:

Cluv

.C ni eno naht ylevol erom margorp a

ees reven llahs I taht kniht I

該程式使用二進位制模式,以便處理MS-DOS文字和UNIX檔案。但是,在使用其他格式文字檔案的環境中可能無法正常工作。

--------------

如果通過命令列環境執行該程式,待處理檔案要和可執行檔案在同一個目錄(或資料夾)中。如果在IDE中執行該程式,具體查詢方案序因實現而異。例如,預設情況下,Microsoft Visual Studio 2012在原始碼所在的目錄中查詢,而Xcode 4.6則在可執行檔案所在的目錄中查詢。

--------------

接下來,我們要討論3個問題:fseek()和ftell()函式的工作原理、如何使用二進位制流、如何讓程式可移植。

1 fseek()和ftell()的工作原理

fseek()的第1個引數是FILE指標,指向待查詢的檔案,fopen()應該已開啟該檔案。

fseek()的第2個引數是偏移量(offset)。該引數表示從起始點開始要移動的距離(參見表13.3列出的起始點模式)。該引數必須是一個long型別的值,可以為正(前移)、負(後移)或0(保持不動)。

fseek()的第3個引數是模式,該引數確定起始點。根據ANSI標準,在stdio.h標頭檔案中規定了幾個表示模式的明示常量(manifest constant),如表13.3所示。

c594820ca1a6d02eb8fdb741bed9f167.png

舊的實現可能缺少這些定義,可以使用數值0L、1L、2L分別表示這3種模式。L字尾表明其值是long型別。或者,實現可能把這些明示常量定義在別的標頭檔案中。如果不確定,請查閱實現的使用手冊或線上幫助。

下面是呼叫fseek()函式的一些示例,fp是一個檔案指標:

fseek(fp, 0L, SEEK_SET);   // go to the beginning of the filefseek(fp, 10L, SEEK_SET);  // go 10 bytes into the filefseek(fp, 2L, SEEK_CUR);   // advance 2 bytes from the current positionfseek(fp, 0L, SEEK_END);   // go to the end of the filefseek(fp, -10L, SEEK_END); // back up 10 bytes from the end of the file

對於這些呼叫還有一些限制,我們稍後再討論。

如果一切正常,fseek()的返回值為0;如果出現錯誤(如試圖移動的距離超出檔案的範圍),其返回值為-1。

ftell()函式的返回型別是long,它返回的是引數指向檔案的當前位置距檔案開始處的位元組數。ANSI-C把它定義在stdio.h中。在最初實現的UNIX中,ftell()通過返回距檔案開始處的位元組數來確定檔案的位置。檔案的第1個位元組到檔案開始處的距離是0,以此類推。ANSI C規定,該定義適用於以二進位制模式開啟的檔案,以文字模式開啟檔案的情況不同。這也是程式reverse.c以二進位制模式開啟檔案的原因。

下面,我們來分析程式reverse.c中的基本要素。首先,下面的語句:

 fseek(fp, 0L, SEEK_END);

把當前位置設定為距檔案末尾0位元組偏移量。也就是說,該語句把當前位置設定在檔案結尾。下一條語句:

last = ftell(fp);

把從檔案開始處到檔案結尾的位元組數賦給last。然後是一個for迴圈:

for (count = 1L; count <= last; count++){  fseek(fp, -count, SEEK_END);    /* go backward */     ch = getc(fp); }

第1輪迭代,把程式定位到檔案結尾的第1個字元(即,檔案的最後一個字元)。然後,程式列印該字元。下一輪迭代把程式定位到前一個字元,並列印該字元。重複這一過程直至到達檔案的第1個字元,並列印。

2 二進位制模式和文字模式

我們設計的程式reverse.c在UNIX和MS-DOS環境下都可以執行。UNIX只有一種檔案格式,所以不需要進行特殊的轉換。然而MS-DOS要格外注意。許多MS-DOS編輯器都用Ctrl+Z標記文字檔案的結尾。以文字模式開啟這樣的檔案時,C能識別這個作為檔案結尾標記的字元。但是,以二進位制模式開啟相同的檔案時,Ctrl+Z字元被看作是檔案中的一個字元,而實際的檔案結尾符在該字元的後面。檔案結尾符可能緊跟在Ctrl+Z字元後面,或者檔案中可能用空字元填充,使該檔案的大小是256的倍數。在DOS環境下不會列印空字元,程式reverse.c中就包含了防止列印Ctrl+Z字元的程式碼。

二進位制模式和文字模式的另一個不同之處是:MS-DOS用組合表示文字檔案換行。以文字模式開啟相同的檔案時,C程式把“看成”。但是,以二進位制模式開啟該檔案時,程式能看見這兩個字元。因此,程式reverse.c中還包含了不列印的程式碼。通常,UNIX文字檔案既沒有Ctrl+Z,也沒有,所以這部分程式碼不會影響大部分UNIX文字檔案。

ftell()函式在文字模式和二進位制模式中的工作方式不同。許多系統的文字檔案格式與UNIX的模型有很大不同,導致從檔案開始處統計的位元組數成為一個毫無意義的值。ANSI C規定,對於文字模式,ftell()返回的值可以作為fseek()的第2個引數。對於MS-DOS,ftell()返回的值把當作一個位元組計數。

3 可移植性

理論上,fseek()和ftell()應該符合UNIX模型。但是,不同系統存在著差異,有時確實無法做到與UNIX模型一致。因此,ANSI對這些函式降低了要求。下面是一些限制。

- 在二進位制模式中,實現不必支援SEEK_END模式。因此無法保證程式清單13.4的可移植性。移植性更高的方法是逐位元組讀取整個檔案直到檔案末尾。C前處理器的條件編譯指令(第16章介紹)提供了一種系統方法來處理這種情況。

- 在文字模式中,只有以下呼叫能保證其相應的行為。

22dc4024be5920198188981dba7f9a43.png

不過,許多常見的環境都支援更多的行為。

4 getpos()和fsetpos()函式

fseek()和ftell()潛在的問題是,它們都把檔案大小限制在long型別能表示的範圍內。也許20億位元組看起來相當大,但是隨著儲存裝置的容量迅猛增長,檔案也越來越大。鑑於此,ANSI C新增了兩個處理較大檔案的新定位函式:fgetpos()和fsetpos()。這兩個函式不使用long型別的值表示位置,它們使用一種新型別:fpos_t(代表file positiontype,檔案定位型別)。fpos_t型別不是基本型別,它根據其他型別來定義。fpos_t型別的變數或資料物件可以在檔案中指定一個位置,它不能是陣列型別,除此之外,沒有其他限制。實現可以提供一個滿足特殊平臺要求的型別,例如,fpos_t可以實現為結構。

ANSI-C定義瞭如何使用fpos_t型別。fgetpos()函式的原型如下:

nt fgetpos(FILE * restrict stream, fpos_t * restrict pos);

呼叫該函式時,它把fpos_t型別的值放在pos指向的位置上,該值描述了檔案中的當前位置距檔案開頭的位元組數。如果成功,fgetpos()函式返回0;如果失敗,返回非0。

fsetpos()函式的原型如下:

: int fsetpos(FILE *stream, const fpos_t *pos);

呼叫該函式時,使用pos指向位置上的fpos_t型別值來設定檔案指標指向偏移該值後指定的位置。如果成功,fsetpos()函式返回0;如果失敗,則返回非0。fpos_t型別的值應通過之前呼叫fgetpos()獲得。