App下載

你知道C語(yǔ)言是如何處理fseek()和ftell()這兩個(gè)I/O隨機(jī)訪問(wèn)數(shù)嗎?

猿友 2020-07-31 14:27:04 瀏覽數(shù) (4803)
反饋

這篇文章中將會(huì)討論到:fseek()ftell()函數(shù)的工作原理、如何使用二進(jìn)制流、如何讓程序可移植。

有了fseek()函數(shù),便可把文件看作是數(shù)組,在fopen()打開的文件中直接移動(dòng)到任意字節(jié)處。我們創(chuàng)建一個(gè)程序reverse.c演示fseek()ftell()的用法。注意,fseek()有3個(gè)參數(shù),返回int類型的值;ftell()函數(shù)返回一個(gè)long類型的值,表示文件中的當(dāng)前位置。

/* reverse.c -- displays a file in reverse order */
#include <stdio.h>
#include <stdlib.h>
#define CNTL_Z '032'   /* eof marker in DOS text files */
#define SLEN 81
int 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;
}

下面是對(duì)一個(gè)文件的輸出:

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

該程序使用二進(jìn)制模式,以便處理MS-DOS文本和UNIX文件。但是,在使用其他格式文本文件的環(huán)境中可能無(wú)法正常工作。

如果通過(guò)命令行環(huán)境運(yùn)行該程序,待處理文件要和可執(zhí)行文件在同一個(gè)目錄(或文件夾)中。如果在IDE中運(yùn)行該程序,具體查找方案序因?qū)崿F(xiàn)而異。例如,默認(rèn)情況下,Microsoft Visual Studio 2012在源代碼所在的目錄中查找,而Xcode 4.6則在可執(zhí)行文件所在的目錄中查找。

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

fseek()的第1個(gè)參數(shù)是FILE指針,指向待查找的文件,fopen()應(yīng)該已打開該文件。

fseek()的第2個(gè)參數(shù)是偏移量(offset)。該參數(shù)表示從起始點(diǎn)開始要移動(dòng)的距離(參見表13.3列出的起始點(diǎn)模式)。該參數(shù)必須是一個(gè)long類型的值,可以為正(前移)、負(fù)(后移)或0(保持不動(dòng))。

fseek()的第3個(gè)參數(shù)是模式,該參數(shù)確定起始點(diǎn)。根據(jù)ANSI標(biāo)準(zhǔn),在stdio.h頭文件中規(guī)定了幾個(gè)表示模式的明示常量(manifest constant),如表13.3所示。

明示標(biāo)量

舊的實(shí)現(xiàn)可能缺少這些定義,可以使用數(shù)值0L、1L、2L分別表示這3種模式。L后綴表明其值是long類型?;蛘?,實(shí)現(xiàn)可能把這些明示常量定義在別的頭文件中。如果不確定,請(qǐng)查閱實(shí)現(xiàn)的使用手冊(cè)或在線幫助。

下面是調(diào)用fseek()函數(shù)的一些示例,fp是一個(gè)文件指針:

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

對(duì)于這些調(diào)用還有一些限制,我們稍后再討論。

如果一切正常,fseek()的返回值為0;如果出現(xiàn)錯(cuò)誤(如試圖移動(dòng)的距離超出文件的范圍),其返回值為-1。

ftell()函數(shù)的返回類型是long,它返回的是參數(shù)指向文件的當(dāng)前位置距文件開始處的字節(jié)數(shù)。ANSI-C把它定義在stdio.h中。在最初實(shí)現(xiàn)的UNIX中,ftell()通過(guò)返回距文件開始處的字節(jié)數(shù)來(lái)確定文件的位置。文件的第1個(gè)字節(jié)到文件開始處的距離是0,以此類推。ANSI C規(guī)定,該定義適用于以二進(jìn)制模式打開的文件,以文本模式打開文件的情況不同。這也是程序reverse.c以二進(jìn)制模式打開文件的原因。

下面,我們來(lái)分析程序reverse.c中的基本要素。首先,下面的語(yǔ)句:

 fseek(fp, 0L, SEEK_END);

把當(dāng)前位置設(shè)置為距文件末尾0字節(jié)偏移量。也就是說(shuō),該語(yǔ)句把當(dāng)前位置設(shè)置在文件結(jié)尾。下一條語(yǔ)句:

last = ftell(fp);

把從文件開始處到文件結(jié)尾的字節(jié)數(shù)賦給last。然后是一個(gè)for循環(huán):

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

第1輪迭代,把程序定位到文件結(jié)尾的第1個(gè)字符(即,文件的最后一個(gè)字符)。然后,程序打印該字符。下一輪迭代把程序定位到前一個(gè)字符,并打印該字符。重復(fù)這一過(guò)程直至到達(dá)文件的第1個(gè)字符,并打印。

2 二進(jìn)制模式和文本模式

我們?cè)O(shè)計(jì)的程序reverse.cUNIXMS-DOS環(huán)境下都可以運(yùn)行。UNIX只有一種文件格式,所以不需要進(jìn)行特殊的轉(zhuǎn)換。然而MS-DOS要格外注意。許多MS-DOS編輯器都用Ctrl+Z標(biāo)記文本文件的結(jié)尾。以文本模式打開這樣的文件時(shí),C能識(shí)別這個(gè)作為文件結(jié)尾標(biāo)記的字符。但是,以二進(jìn)制模式打開相同的文件時(shí),Ctrl+Z字符被看作是文件中的一個(gè)字符,而實(shí)際的文件結(jié)尾符在該字符的后面。文件結(jié)尾符可能緊跟在Ctrl+Z字符后面,或者文件中可能用空字符填充,使該文件的大小是256的倍數(shù)。在DOS環(huán)境下不會(huì)打印空字符,程序reverse.c中就包含了防止打印Ctrl+Z字符的代碼。

二進(jìn)制模式和文本模式的另一個(gè)不同之處是:MS-DOS\r\n組合表示文本文件換行。以文本模式打開相同的文件時(shí),C程序把\r\n“看成”\n。但是,以二進(jìn)制模式打開該文件時(shí),程序能看見這兩個(gè)字符。因此,程序reverse.c中還包含了不打印\r的代碼。通常,UNIX文本文件既沒(méi)有Ctrl+Z,也沒(méi)有\r,所以這部分代碼不會(huì)影響大部分UNIX文本文件。

ftell()函數(shù)在文本模式和二進(jìn)制模式中的工作方式不同。許多系統(tǒng)的文本文件格式與UNIX的模型有很大不同,導(dǎo)致從文件開始處統(tǒng)計(jì)的字節(jié)數(shù)成為一個(gè)毫無(wú)意義的值。ANSI C規(guī)定,對(duì)于文本模式,ftell()返回的值可以作為fseek()的第2個(gè)參數(shù)。對(duì)于MS-DOSftell()返回的值把\r\n當(dāng)作一個(gè)字節(jié)計(jì)數(shù)。

3 可移植性

理論上,fseek()ftell()應(yīng)該符合UNIX模型。但是,不同系統(tǒng)存在著差異,有時(shí)確實(shí)無(wú)法做到與UNIX模型一致。因此,ANSI對(duì)這些函數(shù)降低了要求。下面是一些限制。

  • 在二進(jìn)制模式中,實(shí)現(xiàn)不必支持SEEK_END模式。因此無(wú)法保證程序清單13.4的可移植性。移植性更高的方法是逐字節(jié)讀取整個(gè)文件直到文件末尾。C預(yù)處理器的條件編譯指令(第16章介紹)提供了一種系統(tǒng)方法來(lái)處理這種情況。

  • 在文本模式中,只有以下調(diào)用能保證其相應(yīng)的行為。

不過(guò),許多常見的環(huán)境都支持更多的行為。

4 getpos()和fsetpos()函數(shù)

fseek()ftell()潛在的問(wèn)題是,它們都把文件大小限制在long類型能表示的范圍內(nèi)。也許20億字節(jié)看起來(lái)相當(dāng)大,但是隨著存儲(chǔ)設(shè)備的容量迅猛增長(zhǎng),文件也越來(lái)越大。鑒于此,ANSI C新增了兩個(gè)處理較大文件的新定位函數(shù):fgetpos()fsetpos()。這兩個(gè)函數(shù)不使用long類型的值表示位置,它們使用一種新類型:fpos_t(代表file positiontype,文件定位類型)。fpos_t類型不是基本類型,它根據(jù)其他類型來(lái)定義。fpos_t類型的變量或數(shù)據(jù)對(duì)象可以在文件中指定一個(gè)位置,它不能是數(shù)組類型,除此之外,沒(méi)有其他限制。實(shí)現(xiàn)可以提供一個(gè)滿足特殊平臺(tái)要求的類型,例如,fpos_t可以實(shí)現(xiàn)為結(jié)構(gòu)。

ANSI-C定義了如何使用fpos_t類型。fgetpos()函數(shù)的原型如下:

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

調(diào)用該函數(shù)時(shí),它把fpos_t類型的值放在pos指向的位置上,該值描述了文件中的當(dāng)前位置距文件開頭的字節(jié)數(shù)。如果成功,fgetpos()函數(shù)返回0;如果失敗,返回非0。

fsetpos()函數(shù)的原型如下:

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

調(diào)用該函數(shù)時(shí),使用pos指向位置上的fpos_t類型值來(lái)設(shè)置文件指針指向偏移該值后指定的位置。如果成功,fsetpos()函數(shù)返回0;如果失敗,則返回非0。fpos_t類型的值應(yīng)通過(guò)之前調(diào)用fgetpos()獲得。

以上就是關(guān)于C語(yǔ)言處理I/O的兩個(gè)隨機(jī)訪問(wèn)函數(shù):fseek()ftell()的相關(guān)信息了,希望對(duì)大家有所幫助。想了解更多的知識(shí),大家可以看一下教程

C教程:http://o2fo.com/c/

文章參考來(lái)源:www.toutiao.com/a6855410553498632708

C

0 人點(diǎn)贊