C語言之文件操作

1. 文件操作

1.1. 文件概述

我們對文件的概念已經非常熟悉了,比如常見的 Word 文檔、txt 文件、源文件等。文件是數據源的一種,最主要的作用是保存數據。

在操作系統中,爲了統一對各種硬件的操作,簡化接口,不同的硬件設備也都被看成一個文件。對這些文件的操作,等同於對磁盤上普通文件的操作。例如:

通常把顯示器稱爲標準輸出文件,printf 就是向這個文件輸出數據;
通常把鍵盤稱爲標準輸入文件,scanf 就是從這個文件讀取數據。

常見硬件設備所對應的文件

文件 硬件設備
stdin 標準輸入文件,一般指鍵盤;scanf()、getchar() 等函數默認從 stdin 獲取輸入。
stdin 標準輸出文件,一般指顯示器;printf()、putchar() 等函數默認向 stdout 輸出數據。
stderr 標準錯誤文件,一般指顯示器;perror() 等函數默認向 stderr 輸出數據(後續會講到)。
stdprn 標準打印文件,一般指打印機。

我們不去探討硬件設備是如何被映射成文件的,大家只需要記住,在C語言中硬件設備可以看成文件,有些輸入輸出函數不需要你指明到底讀寫哪個文件,系統已經爲它們設置了默認的文件,當然你也可以更改,例如讓 printf 向磁盤上的文件輸出數據。

1.1.1. 文件

操作文件的正確流程爲:打開文件 --> 讀寫文件 --> 關閉文件。文件在進行讀寫操作之前要先打開,使用完畢要關閉。

所謂打開文件,就是獲取文件的有關信息,例如文件名、文件狀態、當前讀寫位置等,這些信息會被保存到一個 FILE 類型的結構體變量中。關閉文件就是斷開與文件之間的聯繫,釋放結構體變量,同時禁止再對該文件進行操作。

在C語言中,文件有多種讀寫方式,可以一個字符一個字符地讀取,也可以讀取一整行,還可以讀取若干個字節。文件的讀寫位置也非常靈活,可以從文件開頭讀取,也可以從中間位置讀取。

1.1.2. 文件流

所有的文件(保存在磁盤)都要載入內存才能處理,所有的數據必須寫入文件(磁盤)纔不會丟失。數據在文件和內存之間傳遞的過程叫做文件流,類似水從一個地方流動到另一個地方。數據從文件複製到內存的過程叫做輸入流,從內存保存到文件的過程叫做輸出流。

文件是數據源的一種,除了文件,還有數據庫、網絡、鍵盤等;數據傳遞到內存也就是保存到C語言的變量(例如整數、字符串、數組、緩衝區等)。我們把數據在數據源和程序(內存)之間傳遞的過程叫做數據流(Data Stream)。相應的,數據從數據源到程序(內存)的過程叫做輸入流(Input Stream),從程序(內存)到數據源的過程叫做輸出流(Output Stream)。

輸入輸出(Input output,IO)是指程序(內存)與外部設備(鍵盤、顯示器、磁盤、其他計算機等)進行交互的操作。幾乎所有的程序都有輸入與輸出操作,如從鍵盤上讀取數據,從本地或網絡上的文件讀取數據或寫入數據等。通過輸入和輸出操作可以從外界接收信息,或者是把信息傳遞給外界。

我們可以說,打開文件就是打開了一個流。

1.2. 文件的打開函數fopen與關閉函數fclose

1.2.1. 文件的打開(fopen函數)

fopen() 函數用來打開一個文件,它的原型爲:

FILE *fopen(char *filename, char *mode);

filename爲文件名(包括文件路徑),mode爲打開方式,它們都是字符串。fopen() 會獲取文件信息,包括文件名、文件狀態、當前讀寫位置等,並將這些信息保存到一個FILE類型的結構體變量中,然後將該變量的地址返回。

FILE是在stdio.h頭文件中定義的一個結構體,用來保存文件信息。

如果希望接收 fopen() 的返回值,就需要定義一個 FILE 類型的指針。例如:

FILE *fp = fopen("demo.txt", "r");
表示以“只讀”方式打開當前目錄下的 demo.txt 文件,並使 fp 指向該文件,這樣就可以通過 fp 來操作 demo.txt 了。fp 通常被稱爲文件指針。又如:

FILE *fp = fopen("demo.txt","rb");
表示以二進制方式打開 D 盤下的 demo.txt 文件,允許讀和寫。

打開方式(mode)有多種,見下表:

打開方式 說明
r 以只讀方式打開文件,只允許讀取,不允許寫入。該文件必須存在。
r+ 以讀/寫方式打開文件,允許讀取和寫入。該文件必須存在。
rb+ 以讀/寫方式打開一個二進制文件,允許讀/寫數據。
rt+ 以讀/寫方式打開一個文本文件,允許讀和寫。
w 以只寫方式打開文件,若文件存在則長度清爲0,即該文件內容消失,若不存在則創建該文件。
w+ 以讀/寫方式打開文件,若文件存在則文件長度清爲零,即該文件內容會消失。若文件不存在則建立該文件。
a 以追加的方式打開只寫文件。若文件不存在,則會建立該文件,如果文件存在,寫入的數據會被加到文件尾,即文件原先的內容會被保留(EOF符保留)。
a+ 以追加方式打開可讀/寫的文件。若文件不存在,則會建立該文件,如果文件存在,則寫入的數據會被加到文件尾後,即文件原先的內容會被保留(原來的EOF符 不保留)。
wb 以只寫方式打開或新建一個二進制文件,只允許寫數據
wb+ 以讀/寫方式打開或建立一個二進制文件,允許讀和寫。
wt+ 以讀/寫方式打開或建立一個文本文件,允許讀寫。
at+ 以讀/寫方式打開一個文本文件,允許讀或在文本末追加數據。
ab+ 以讀/寫方式打開一個二進制文件,允許讀或在文件末追加數據。

幾點說明

  1. 文件打開方式由r、w、a、t、b、+ 六個字符拼成,各字符的含義是:
    r(read):讀
    w(write):寫
    a(append):追加
    t(text):文本文件,可省略不寫
    b(banary):二進制文件
    +:讀和寫

  2. 如果沒有“b”字符,文件以文本方式打開。

  3. 凡用“r”打開一個文件時,該文件必須已經存在。

  4. 在打開一個文件時,如果出錯,fopen將返回一個空指針值NULL。在程序中可以用這一信息來判別是否完成打開文件的工作,並作相應的處理。因此常用以下程序段打開文件:

if( (fp=fopen("demo.txt","rb") == NULL ){
    printf("Error on open demo.txt file!");
    getch();
    exit(1);
}

這段程序的意義是,如果返回的指針爲空,表示不能打開D盤根目錄下的 demo.txt 文件,並給出提示信息“error on open demo.txt file!”。第3行getch()的功能是從鍵盤輸入一個字符,但不在屏幕上顯示。在這裏,該行的作用是等待,只有當用戶從鍵盤敲任一鍵時,程序才繼續執行,因此用戶可利用這個等待時間閱讀出錯提示。敲鍵後執行exit(1)退出程序。

  1. 把一個文本文件讀入內存時,要將ASCII碼轉換成二進制碼,而把文件以文本方式寫入磁盤時,也要把二進制碼轉換成ASCII碼,因此文本文件的讀寫要花費較多的轉換時間。對二進制文件的讀寫不存在這種轉換。

  2. 標準輸入文件 stdin(鍵盤)、標準輸出文件 stdout(顯示器)、標準錯誤文件 stderr(顯示器)是由系統打開的,可直接使用。

1.2.2. 文件關閉(fclose函數)

文件一旦使用完畢,應該用fclose()函數把文件關閉,以釋放相關資源,避免數據丟失。fclose() 的原型爲:

int fclose(FILE *fp);
fp 爲文件指針。例如:

fclose(fp);
文件正常關閉時,fclose() 的返回值爲0,如果返回非零值則表示有錯誤發生。

1.3. 文件的狀態,包括feof函數和ferror函數;

對EOF的說明
EOF 本來表示文件末尾,意味着讀取結束,但是很多函數在讀取出錯時也返回 EOF,那麼當返回EOF時,到底是文件讀取完畢了還是讀取出錯了?我們可以藉助 stdio.h 中的兩個函數來判斷,分別是 feof() 和 ferror()。

1.3.1. feof()函數

feof() 函數用來判斷文件內部指針是否指向了文件末尾,它的原型是:

int feof ( FILE * fp );

當指向文件末尾時返回非零值,否則返回零值。

1.3.2. ferror()函數

ferror() 函數用來判斷文件操作是否出錯,它的原型是:

int ferror ( FILE *fp );

出錯時返回非零值,否則返回零值。

需要說明的是,文件出錯是非常少見的情況,上面的示例基本能夠保證將文件內的數據讀取完畢。如果追求完美,也可以加上判斷並給出提示:

#include<stdio.h>
int main(){
    FILE *fp;
    char ch;
  
    //如果文件不存在,給出提示並退出
    if( (fp=fopen("demo.txt","rt")) == NULL ){
        printf("Cannot open file, press any key to exit!");
        getch();
        exit(1);
    }
    //每次讀取一個字節,直到讀取完畢
    while( (ch=fgetc(fp)) != EOF ){
        putchar(ch);
    }
    
    if (!feof(fp) )
        fprintf( stderr, "Error reading.\n" );  //perror("Error reading");
    
    putchar('\n');  //輸出換行符
    if(ferror(fp)){
        puts("Error reading");
    }else{
        puts("Success reading");
    }
    fclose(fp);
    return 0;
}

1.4. 文件的讀/寫,包括fread和fwrite函數、fputc和fgetc函數、fgets與fputs函數的應用

在C語言中,讀寫文件比較靈活,既可以每次讀寫一個字符,也可以讀寫一個字符串,甚至是任意字節的數據(數據塊)。

1.4.1. 以字符形式讀寫文件

以字符形式讀寫文件時,每次可以從文件中讀取一個字符,或者向文件中寫入一個字符。主要使用兩個函數:fgetc()和fputc()。

1.4.1.1. 字符讀取函數 fgetc

fgetc 是 file get char 的縮寫,意思是從指定的文件中讀取一個字符。它的原型爲:

int fgetc (FILE *fp);

fp 爲文件指針。fgetc() 讀取成功時返回讀取到的字符,讀取到文件末尾或讀取失敗時返回EOF。

EOF 是 end of file 的縮寫,表示文件末尾,是在 stdio.h 中定義的宏,它的值是一個負數,往往是 -1。返回值類型之所以爲 int,就是爲了容納這個負數(char不能是負數)。

注:EOF 不絕對是 -1,也可以是其他負數,這要看編譯器的實現。

fgetc() 使用舉例:

char ch;
FILE *fp = fopen("demo.txt", "r+");
ch = fgetc(fp);

表示從demo.txt文件中讀取一個字符,並保存到變量ch中。

在文件內部有一個位置指針,用來指向當前讀寫到的位置,也就是讀寫到第幾個字節。在文件打開時,該指針總是指向文件的第一個字節。使用fgetc 函數後,該指針會向後移動一個字節,所以可以連續多次使用fgetc讀取多個字符。

注意:這個文件內部的位置指針與C語言中的指針不是一回事。位置指針僅僅是一個標誌,表示文件讀寫到的位置,也就是讀寫到第幾個字節,它不表示地址。文件每讀寫一次,位置指針就會移動一次,它不需要你在程序中定義和賦值,而是由系統自動設置,對用戶是透明的。

1.4.1.2. 字符寫入函數fputc

fputc 是 file output char 的所以,意思是向指定的文件中寫入一個字符。調用的形式爲:

int fputc ( int ch, FILE *fp );

ch 爲要寫入的字符,fp 爲文件指針。fputc() 寫入成功時返回寫入的字符,失敗時返回EOF,返回值類型爲 int 也是爲了容納這個負數。例如:

fputc('a', fp);
或者:
char ch = 'a'; fputc(ch, fp);

表示把字符 ‘a’ 寫入fp所指向的文件中。

兩點說明

  1. 被寫入的文件可以用寫、讀寫、追加方式打開,用寫或讀寫方式打開一個已存在的文件時將清除原有的文件內容,並將寫入的字符放在文件開頭。如需保留原有文件內容,並把寫入的字符放在文件末尾,就必須以追加方式打開文件。不管以何種方式打開,被寫入的文件若不存在時則創建該文件。

  2. 每寫入一個字符,文件內部位置指針向後移動一個字節。

【示例】從鍵盤輸入一行字符,寫入文件。

#include<stdio.h>
int main(){
    FILE *fp;
    char ch;
    //判斷文件是否成功打開
    if( (fp=fopen("demo.txt","wt+")) == NULL ){
        printf("Cannot open file, press any key to exit!\n");
        getch();
        exit(1);
    }
    printf("Input a string:\n");
    //每次從鍵盤讀取一個字符並寫入文件
    while ( (ch=getchar()) != '\n' ){
        fputc(ch,fp);
    }
    fclose(fp);
    return 0;
}

運行程序,輸入一行字符並按回車鍵結束,打開D盤下的demo.txt文件,就可以看到剛纔輸入的內容。

程序每次從鍵盤讀取一個字符並寫入文件,直到按下回車鍵,while 條件不成立,結束讀取。

1.4.2. 以字符串形式讀寫文件

fgetc() 和 fputc() 函數每次只能讀寫一個字符,速度較慢;實際開發中往往是每次讀寫一個字符串或者一個數據塊,這樣能明顯提高效率。

1.4.2.1. 讀字符串函數fgets

fgets() 函數用來從指定的文件中讀取一個字符串,並保存到字符數組中,它的原型爲:

char *fgets ( char *str, int n, FILE *fp );

str 爲字符數組,n 爲要讀取的字符數目,fp 爲文件指針。

返回值:讀取成功時返回字符數組首地址,也即 str;讀取失敗時返回 NULL;如果開始讀取時文件內部指針已經指向了文件末尾,那麼將讀取不到任何字符,也返回 NULL。

注意: 讀取到的字符串會在末尾自動添加 ‘\0’,n 個字符也包括 ‘\0’。也就是說,實際只讀取到了 n-1 個字符,如果希望讀取 100 個字符,n 的值應該爲 101。例如:

#define N 101
char str[N];
FILE *fp = fopen("demo.txt", "r");
fgets(str, N, fp);

表示從 demo.txt 中讀取100個字符,並保存到字符數組str中。

需要重點說明的是,在讀取到 n-1 個字符之前如果出現了換行,或者讀到了文件末尾,則讀取結束。這就意味着,不管n的值多大,fgets() 最多隻能讀取一行數據,不能跨行。在C語言中,沒有按行讀取文件的函數,我們可以藉助 fgets(),將n的值設置地足夠大,每次就可以讀取到一行數據。

fgets() 遇到換行時,會將換行符一併讀取到當前字符串。該示例的輸出結果之所以和 demo.txt 保持一致,該換行的地方換行,就是因爲 fgets() 能夠讀取到換行符。而 gets() 不一樣,它會忽略換行符。

【示例】一行一行地讀取文件

#include <stdio.h>
#include <stdlib.h>
#define N 100
int main(){
    FILE *fp;
    char str[N+1];
    if( (fp=fopen("demo.txt","rt")) == NULL ){
        printf("Cannot open file, press any key to exit!\n");
        getch();
        exit(1);
    }
   
    while(fgets(str, N, fp) != NULL){
        printf("%s", str);
    }
    fclose(fp);
    system("pause");
    return 0;
}

1.4.2.2. 寫字符串函數fputs

fputs() 函數用來向指定的文件寫入一個字符串,它的原型爲:

int fputs( char *str, FILE *fp );

str 爲要寫入的字符串,fp 爲文件指針。寫入成功返回非負數,失敗返回EOF。例如:

char *str = "http://c.biancheng.net";
FILE *fp = fopen("demo.txt", "at+");
fputs(str, fp);

表示把把字符串 str 寫入到 demo.txt 文件中。

【示例】向上例中建立的 demo.txt 文件中追加一個字符串。

#include<stdio.h>
int main(){
    FILE *fp;
    char str[102] = {0}, strTemp[100];
    if( (fp=fopen("demo.txt", "at+")) == NULL ){
        printf("Cannot open file, press any key to exit!\n");
        getch();
        exit(1);
    }
    printf("Input a string:");
    gets(strTemp);
    strcat(str, "\n");
    strcat(str, strTemp);
    fputs(str, fp);
    fclose(fp);
    return 0;
}

運行程序,輸入C C++ Java Linux Shell,打開 demo.txt,文件內容爲:

C語言中文網
http://c.biancheng.net
一個學習編程的好網站!
C C++ Java Linux Shell

1.4.3. 以數據塊形式讀寫文件

fgets() 有侷限性,每次最多隻能從文件中讀取一行內容,因爲 fgets 遇到換行符就結束讀取。如果希望讀取多行內容,需要使用 fread 函數;相應地寫入函數爲 fwrite。

1.4.3.1. fread() 函數

fread() 函數用來從指定文件中讀取塊數據。所謂塊數據,也就是若干個字節的數據,可以是一個字符,可以是一個字符串,可以是多行數據,並沒有什麼限制。fread() 的原型爲:

size_t fread ( void *ptr, size_t size, size_t count, FILE *fp );

1.4.3.2. fwrite() 函數

fwrite() 函數用來向文件中寫入塊數據,它的原型爲:

size_t fwrite ( void * ptr, size_t size, size_t count, FILE *fp );

對參數的說明:
(1) ptr 爲內存區塊的指針,它可以是數組、變量、結構體等。fread() 中的 ptr 用來存放讀取到的數據,fwrite() 中的 ptr 用來存放要寫入的數據。
(2) size:表示每個數據塊的字節數。
(3) count:表示要讀寫的數據塊的塊數。
(4) fp:表示文件指針。

理論上,每次讀寫 size*count 個字節的數據。

size_t 是在 stddef.h 頭文件中使用 typedef 定義的數據類型,表示無符號整數,也即非負數,常用來表示數量。

返回值:返回成功讀寫的塊數,也即 count。
如果返回值小於 count:

對於 fwrite() 來說,肯定發生了寫入錯誤,可以用 ferror() 函數檢測。

對於 fread() 來說,可能讀到了文件末尾,可能發生了錯誤,可以用 ferror() 或 feof() 檢測。

【示例】從鍵盤輸入一個數組,將數組寫入文件再讀取出來。

#include<stdio.h>
#define N 5
int main(){
    //從鍵盤輸入的數據放入a,從文件讀取的數據放入b
    int a[N], b[N];
    int i, size = sizeof(int);
    FILE *fp;
    if( (fp=fopen("demo2.txt", "rb+")) == NULL ){
        printf("Cannot open file, press any key to exit!\n");
        getch();
        exit(1);
    }
  
    //從鍵盤輸入數據 並保存到數組a
    for(i=0; i<N; i++){
        scanf("%d", &a[i]);
    }
    //將數組a的內容寫入到文件
    fwrite(a, size, N, fp);
    //將文件中的位置指針重新定位到文件開頭
    rewind(fp);
    //從文件讀取內容並保存到數組b
    fread(b, size, N, fp);
    //在屏幕上顯示數組b的內容
    for(i=0; i<N; i++){
        printf("%d ", b[i]);
    }
    printf("\n");
    fclose(fp);
    return 0;
}

運行結果:

23 409 500 100 222↙
23 409 500 100 222

fwrite()/fread() 函數直接操作字節,建議使用二進制方式打開文件。

打開 demo.txt,發現文件內容根本無法閱讀。這是因爲我們使用"rb+"方式打開文件,數據以二進制形式寫入文件,一般無法閱讀。

數據寫入完畢後,位置指針在文件的末尾,要想讀取數據,必須將文件指針移動到文件開頭,這就是rewind(fp);的作用。更多關於rewind函數的內容.

文件的後綴不一定是 .txt,它可以是任意的,你可以自己命名,例如 demo.ddd、demo.doc、demo.diy 等。

【示例】從鍵盤輸入兩個學生數據,寫入一個文件中,再讀出這兩個學生的數據顯示在屏幕上。

#include<stdio.h>
#define N 2
struct stu{
    char name[10]; //姓名
    int num;  //學號
    int age;  //年齡
    float score;  //成績
}boya[N], boyb[N], *pa, *pb;
int main(){
    FILE *fp;
    int i;
    pa = boya;
    pb = boyb;
    if( (fp=fopen("demo.txt", "wb+")) == NULL ){
        printf("Cannot open file, press any key to exit!\n");
        getch();
        exit(1);
    }
    //從鍵盤輸入數據
    printf("Input data:\n");
    for(i=0; i<N; i++,pa++){
        scanf("%s %d %d %f",pa->name, &pa->num,&pa->age, &pa->score);
    }
    //將數組 boya 的數據寫入文件
    fwrite(boya, sizeof(struct stu), N, fp);
    //將文件指針重置到文件開頭
    rewind(fp);
    //從文件讀取數據並保存到數據 boyb
    fread(boyb, sizeof(struct stu), N, fp);
    //輸出數組 boyb 中的數據
    for(i=0; i<N; i++,pb++){
        printf("%s  %d  %d  %f\n", pb->name, pb->num, pb->age, pb->score);
    }
    fclose(fp);
    return 0;
}

運行結果:

Input data:
Tom 2 15 90.5↙
Hua 1 14 99↙
Tom  2  15  90.500000
Hua  1  14  99.000000

1.5. 文件的輸入函數fscanf和輸出函數fprintf的應用

fscanf() 和 fprintf() 函數與前面使用的 scanf() 和 printf() 功能相似,都是格式化讀寫函數,兩者的區別在於 fscanf() 和 fprintf() 的讀寫對象不是鍵盤和顯示器,而是磁盤文件。

這兩個函數的原型爲:

int fscanf ( FILE *fp, char * format, ... ); int fprintf ( FILE *fp, char * format, ... );
fp 爲文件指針,format 爲格式控制字符串,… 表示參數列表。與 scanf() 和 printf() 相比,它們僅僅多了一個 fp 參數。例如:

FILE *fp;
int i, j;
char *str, ch;
fscanf(fp, "%d %s", &i, str);
fprintf(fp,"%d %c", j, ch);

fprintf() 返回成功寫入的字符的個數,失敗則返回負數。fscanf() 返回參數列表中被成功賦值的參數個數。

【示例】用 fscanf 和 fprintf 函數來完成對學生信息的讀寫。

#include<stdio.h>
#define N 2
struct stu{
    char name[10];
    int num;
    int age;
    float score;
} boya[N], boyb[N], *pa, *pb;
int main(){
    FILE *fp;
    int i;
    pa=boya;
    pb=boyb;
    if( (fp=fopen("demo.txt","wt+")) == NULL ){
        printf("Cannot open file, press any key exit!");
        getch();
        exit(1);
    }
    //從鍵盤讀入數據,保存到boya
    printf("Input data:\n");
    for(i=0; i<N; i++,pa++){
        scanf("%s %d %d %f", pa->name, &pa->num, &pa->age, &pa->score);   
    }
    pa = boya;
    //將boya中的數據寫入到文件
    for(i=0; i<N; i++,pa++){
        fprintf(fp,"%s %d %d %f\n", pa->name, pa->num, pa->age, pa->score);   
    }
    //重置文件指針
    rewind(fp);
    //從文件中讀取數據,保存到boyb
    for(i=0; i<N; i++,pb++){
        fscanf(fp, "%s %d %d %f\n", pb->name, &pb->num, &pb->age, &pb->score);
    }
    pb=boyb;
    //將boyb中的數據輸出到顯示器
    for(i=0; i<N; i++,pb++){
        printf("%s  %d  %d  %f\n", pb->name, pb->num, pb->age, pb->score);
    }
    fclose(fp);
    return 0;
}

運行結果:

Input data:
Tom 2 15 90.5↙
Hua 1 14 99↙
Tom  2  15  90.500000
Hua  1  14  99.000000

打開 demo.txt,發現文件的內容是可以閱讀的,格式非常清晰。用 fprintf() 和 fscanf() 函數讀寫配置文件、日誌文件會非常方便,不但程序能夠識別,用戶也可以看懂,可以手動修改。

如果將 fp 設置爲 stdin,那麼 fscanf() 函數將會從鍵盤讀取數據,與 scanf 的作用相同;設置爲 stdout,那麼 fprintf() 函數將會向顯示器輸出內容,與 printf 的作用相同。例如:

#include<stdio.h>
int main(){
    int a, b, sum;
    fprintf(stdout, "Input two numbers: ");
    fscanf(stdin, "%d %d", &a, &b);
    sum = a + b;
    fprintf(stdout, "sum=%d\n", sum);
    return 0;
}

運行結果:

Input two numbers: 10 20↙
sum=30

1.6. 文件的定位,包括rewind函數和fseek函數以及ftell函數的應用

1.6.1. 文件隨機讀寫

實現隨機讀寫的關鍵是要按要求移動位置指針,這稱爲文件的定位。
移動文件內部位置指針的函數主要有兩個,即 rewind() 和 fseek()。

1.6.1.1. 文件定位函數rewind()

rewind() 用來將位置指針移動到文件開頭,前面已經多次使用過,它的原型爲:
void rewind ( FILE *fp );

1.6.1.2. 文件定位函數fseek()

fseek()用來將位置指針移動到任意位置,它的原型爲:
int fseek ( FILE *fp, long offset, int origin );
參數說明:

  1. fp 爲文件指針,也就是被移動的文件。
  2. offset 爲偏移量,也就是要移動的字節數。之所以爲 long 類型,是希望移動的範圍更大,能處理的文件更大。
  3. origin 爲起始位置,也就是從何處開始計算偏移量。C語言規定的起始位置有三種,分別爲文件開頭、當前位置和文件末尾,每個位置都用對應的常量來表示:
起始點 常量名 常量值
文件開頭 SEEK_SET 0
當前位置 SEEK_CUR 1
文件末尾 SEEK_END 2

1.6.1.3. 獲取文件當前訪問位置函數ftell()

ftell()用來返回當前文件訪問位置,它的原型爲:
long ftell( FILE * fp );

如果發生錯誤ftell( )將返回-1.

下面是一個小demo,三個函數都有涉及:

#include<stdio.h>
#define MAX_LINE 256
int main(int argc,char *argv[]){

    FILE *fp;
    long lOffset = 0L;
    char sLine[MAX_LINE] = "";
    char *result = NULL;
    int lineno = 0;
    /* ... */
    if ((fp = fopen(argv[2], "r")) == NULL){
        fprintf(stderr, "Unable to open file %s\n", argv[2]);
        exit( -1 );
    }
    do{
        lOffset = ftell( fp ); // Bookmark the beginning of
        // the line we're about to read.
        if ( -1L == lOffset )
            fprintf( stderr, "Unable to obtain offset in %s\n", argv[2] );
        else
            lineno++;
        if (!fgets( sLine, MAX_LINE, fp ) ) // Read next line from file.
        {
            fprintf( stderr, "Unable to read from %s\n", argv[2] );
            break;
        }
    } while ( strstr( sLine, argv[1] ) == NULL ); // Test for argument in sLine.
    /* Dropped out of loop: Found search keyword or EOF */
    if ( feof( fp ))
    {
        fprintf( stderr, "Unable to find \"%s\" in %s\n", argv[1], argv[2] );
        rewind( fp );
    }
    else
    {
        printf( "%s (%d): %s\n", argv[2], lineno, sLine );
        fseek( fp, lOffset, 0 ); // Set file pointer at beginning of
    // the line containing the keyword
    }

    return 0;
}

本文未能對C語言的文件I/O操作的每個函數詳細解讀,請諒解.

參考資料:
http://c.biancheng.net/cpp/u/c13/
C: In a Nutshell(c語言核心技術)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章