1. 程式人生 > >C指標程式設計之道(四)- 指向檔案型別的指標

C指標程式設計之道(四)- 指向檔案型別的指標

檔案結構體和檔案指標

// 在c語言中,對檔案的操作一般是用庫函式來實現的
// ANSI(美國國家標準學會)規定了標準的輸入輸出函式,用他們對檔案進行讀寫以規範和提高程式設計效率
// 每個被使用的檔案在記憶體中都會開闢一個區,用來存放檔案的有關資訊,而這些資訊都儲存在檔案結構體FILE中
// FILE 用於訪問一個流,若同時激活了幾個流,那麼每個流都有一個相應的FILE與之相關聯
// 為了在流上執行一些操作,需要呼叫一些合適的庫函式,並傳遞一個與流相關的FILE引數
// C語言定義了三個特別的檔案指標常數,標準輸入、標準輸出、標準錯誤,流的名稱分別為stdin、stdout、stderr,他們都是一個指向FILE的指標
typedef struct { short level; //緩衝區,“滿”或“空”的程度 unsigned flags; //檔案狀態的標誌 char fd; //檔案描述符 unsigned char hold; //如無緩衝區不讀取字元 short basize; //緩衝區的大小 unsigned char *buffer; //資料緩衝區的位置 unsigned char *curp; //指標當前的指向 unsigned istemp; //臨時檔案,指示器
short token; //用於有效性的檢驗 } FILE; // 有了結構體FILE,我們就可以用它來定義FILE型別的變數,用來存放待處理檔案的資訊 FILE arrayf[10]; // 我們還可以定義一個檔案型別的指標,即指向檔案的指標 FILE *fp;

標準流的I/O函式

資料型別     輸入       輸出             描述
----------------------------------------------------------
字元       getchar     putchar     讀取(寫入)單個字元
文字行      fgets       fputs
文字行未格式化的輸入(輸出) scanf printf 文字行格式化的輸入(輸出) ---------------------------------------------------------- # include <stdio.h> int main() { char ch; //char s[10]; //printf("請輸入單個字元: "); //ch = getchar(); //putchar(ch); //printf("請輸入單個字串: "); //fgets(s, sizeof(s)/sizeof(char), stdin); //fputs(s, stdout); printf("請輸入一個字元: "); scanf(" %c", &ch); printf("%c\n", ch); return 0; }

常見檔案操作函式

開啟與關閉:fopen,fclose
讀寫字元:fgetc,fputc
讀寫字串:fgets,fputs
按資料塊讀寫:fread,fwrite
格式化讀寫:fscanffprintf
檔案定位:fseek,rewind,ftell
判斷檔案是否結尾:feof

fopen
函式原型:FILE * fopen(char const *name, char *mode)
相關解釋:用於開啟一個特定的檔案,並把一個流和這個檔案相關聯

fclose
函式原型:int fclose(FILE *fp)
相關解釋:用於關閉一個特定的檔案,所謂關閉就是使檔案指標變數不再指向該檔案,之後不能再通過該指標變數對檔案進行讀寫操作。
         對於輸出流,fclose函式在檔案關閉之前會重新整理緩衝區,這裡需要注意,在程式結束之前,我們一定要關閉所有已經開啟
         的檔案,否則會產生資料的丟失。比如,在向檔案寫資料時,總是先將資料存到緩衝區,待緩衝區滿後才將資料寫入檔案,
         若緩衝區未滿時程式結束,則緩衝區的資料將會丟失。使用fclose函式關閉檔案,他會將緩衝區的資料寫入檔案,然後才
         釋放檔案指標,從而可以避免檔案的丟失。fclose有一個int型別的返回值,檔案關閉成功時返回0,失敗時返回EOF(-1)

fgetc
函式原型:int fgetc(FILE *fp)
相關解釋:從指定的檔案讀取一個字元,該檔案必須是以只讀或讀寫的方式開啟的。fgetc從fp中讀出一個字元並返回這個字元的ASCII碼,
        若讀到檔案結束符,則函式返回檔案結束標誌。

fputc
函式原型:int fputc(int c, FILE *fp)
相關解釋:把一個字元寫到檔案中,若寫入成功則返回寫入字元的ASCII碼,若失敗則返回EOF。

fgets
函式原型:char * fgets(char *s, int n, FILE *fp)
相關解釋:從fp指向的檔案中讀取字元,當讀到回車符、檔案末尾、或滿n-1個字元時 就在字串末尾加上'\0',把他們存放到字元陣列s
        中後函式返回,若在任何字元讀取錢就達到了檔案末尾,fgets就返回NULL指標(用於檢查是否到達檔案尾),否則返回s的首地址

fputs
函式原型:int * fputs(const char *s, FILE *fp)
相關解釋:向指定的檔案中輸入一個字串,若寫入成功則返回0,否則返回EOF。

fread
函式原型:unsigned fread(void *s, unsigned size, unsigned count, FILE *fp)
相關解釋:從fp指向的檔案中讀取二進位制資料塊到s陣列,函式有四個引數
        第一個引數s是從檔案中讀物資料的存放地址,
        第二個引數size是要讀的位元組數的大小,
        第三個引數count是要讀取多少個size位元組的資料,即最多允許讀取的資料塊的個數,
        第四個引數fp為指向要讀取的檔案的指標

fwrite
函式原型:unsigned fwrite(void *s, unsigned size, unsigned count, FILE *fp)
相關解釋:將二進位制資料塊s寫入到fp指向的檔案,函式有四個引數
        第一個引數s是要向檔案寫入的資料的存放地址,
        第二個引數size是要寫的位元組數的大小,
        第三個引數count是要寫多少個size位元組的資料,即最多允許寫的資料塊的個數,
        第四個引數fp為指向要寫入的檔案的指標
        返回值1表示寫入成功,0表示寫入失敗

fprintf
函式原型:int fprintf(FILE *fp, const char *format, ...)
相關解釋:fprintf函式與printf函式相似,都是格式化輸出函式,不同的是printf輸出的位置是終端,而fprintf輸出的位置是檔案

fscanf
函式原型:int fscanf(FILE *fp, const char *format, ...)
相關解釋:fscanf函式與scanf函式相似,都是格式化輸入函式,不同的是scanf輸入的位置是終端,而fscanf輸入的位置是檔案
        [注] 用fprintffscanf對檔案進行讀寫,在讀入時需要將ASCII碼轉化成二進位制數,在輸出時又需將二進位制數轉化為
            字元,時間開銷會比較大,因此交換資料比較頻繁的情況下最好不要用這兩個函式。

fseek
函式原型:int fseek(FILE *fp, long offset, int from)
相關解釋:FILE中有一個指向當前位置的位置指標,如果順序讀寫檔案,每次讀取一個字元,讀完一個字元之後,位置指標自動移動到下
         一個字元。此外我們可以通過函式改變這個指標的位置實現對檔案的隨機讀寫,這樣的函式就是檔案定位函式,fseek就是這
         樣一個檔案定位函式,該函式可以改變檔案的位置指標所指向位置,也就是改變下一個讀取或寫入位置。fseek有三個引數,
         第一個引數fp指向要操作檔案的指標;
         第二個引數offset是位移量,它是以起始點為基點,向前移動的位元組數;
         第三個引數from為開始定位的起始點。
         其中from的取值可以有如下三個(offset可以為負數):
             0    SEEK_SET    定位起始點為檔案的開始
             1    SEEK_CUR    定位起始點為檔案位置指標當前的位置
             2    SEEK_END    定位起始點為檔案的末尾

rewind
函式原型:void rewind(FILE *fp)
相關解釋:rewind為檔案定位函式,作用是使檔案的位置指標重新返回到檔案的開頭,它同時清除流的錯誤提示標誌。

ftell
函式原型:long ftell(FILE *fp)
相關解釋:得到檔案指標的當前位置,用相對檔案開頭的位移量來表示,出錯時返回-1L,這個函式可以讓你儲存一個檔案的當前位置,這樣
        可以在將來再次返回到這個位置。在二進位制檔案中,返回值就是當前位置到檔案頭的位元組數;在文字檔案中,不一定是當前位置到
        檔案頭的位元組數,因為有的系統會對行末字元進行轉換。

feof
函式原型:int feof(FILE *fp)
相關解釋:用於判斷檔案是否結束,當檔案指標指向fp末尾時,函式返回真,否則返回0

相關測試:

/**
* 複製檔案,一次讀取一個字元
*/

# include <stdio.h>
# include <stdlib.h>

int main() {
    FILE *fpr, *fpw;
    FILE * open_file(const char *, const char *);

    //開啟檔案
    fpr = open_file("/Users/zhangqingli/Desktop/test.txt", "r");
    fpw = open_file("/Users/zhangqingli/Desktop/test2.txt", "w");

    //複製原檔案內容到目標檔案,每次只讀取一個字元
    char ch;
    while ((ch=fgetc(fpr)) != EOF) {
        printf("%c", ch);
        fputc(ch, fpw);
    }

    //關閉檔案
    fclose(fpr);
    fclose(fpw);

    return 0;
}

//開啟一個檔案
FILE * open_file(const char *filename, const char *mode) {
    FILE * fp = fopen(filename, mode);
    if (fp==NULL) {
        printf("檔案開啟失敗!\n");
        exit(1);
    }
    return fp;
}



/**
* 複製檔案,一次讀取或寫入一個字串
*/

# include <stdio.h>
# include <stdlib.h>

# define BUFFER_SIZE 10

int main() {
    FILE *fpr, *fpw;
    FILE * open_file(const char *, const char *);

    //開啟檔案
    fpr = open_file("/Users/zhangqingli/Desktop/test.txt", "r");
    fpw = open_file("/Users/zhangqingli/Desktop/test2.txt", "w");

    //複製原檔案內容到目標檔案,一次複製一個字串
    char buffer[BUFFER_SIZE];
    while ((fgets(buffer, BUFFER_SIZE, fpr))) {
        printf("%s", buffer);
        fputs(buffer, fpw);
    }

    //關閉檔案
    fclose(fpr);
    fclose(fpw);

    return 0;
}

//開啟一個檔案
FILE * open_file(const char *filename, const char *mode) {
    FILE * fp = fopen(filename, mode);
    if (fp==NULL) {
        printf("檔案開啟失敗!\n");
        exit(1);
    }
    return fp;
}



/**
* 從鍵盤輸入3個關於圖書的資料,存入檔案,再從檔案中取出資料,顯示到控制檯上
*/

# include <stdio.h>
# include <stdlib.h>

# define MIX_SIZE 3

struct book_info {
    char name[20];
    char author[20];
} book_array[MIX_SIZE];

FILE * open_file(const char *filename, const char *mode);
int save_book_info(const char *filename);
int display_book_info(const char *filename);

int main() {
    //初始化圖書資訊
    printf("初始化圖書的資訊如下\n");
    for (int i=0; i<MIX_SIZE; i++) {
        printf("請輸入圖書名稱:");
        scanf(" %s", book_array[i].name);
        printf("請輸入圖書的作者:");
        scanf(" %s", book_array[i].author);
    }

    //core
    const char *filename = "/Users/zhangqingli/Desktop/test.txt";
    if (save_book_info(filename)) {
        display_book_info(filename);
    }

    return 0;
}

//開啟一個檔案
FILE * open_file(const char *filename, const char *mode) {
    FILE * fp = fopen(filename, mode);
    if (fp==NULL) {
        printf("檔案開啟失敗!\n");
        exit(1);
    }
    return fp;
}

//儲存圖書資訊到檔案
int save_book_info(const char *filename) {
    FILE * fpw;

    //開啟要寫的檔案
    fpw = open_file(filename, "w");

    //將圖書資訊存入待寫入檔案
    for (int i=0; i<MIX_SIZE; i++) {
        int status = (int) fwrite(&book_array[i], sizeof(struct book_info), 1, fpw);
        if (status != 1) {
            printf("檔案寫入出錯!\n");
            return 0;
        }
    }

    //關閉檔案
    fclose(fpw);
    return 1;
}

//從檔案讀取圖書資訊到控制檯
int display_book_info(const char *filename) {
    FILE * fpr;

    //開啟要讀取的檔案
    fpr = open_file(filename, "r");

    //將檔案中的資料讀出並顯示
    for (int i=0; i<MIX_SIZE; i++) {
        fread(&book_array[i], sizeof(struct book_info), 1, fpr);
        printf("%s\t%s\n", book_array[i].name, book_array[i].author);
    }

    //檔案關閉
    fclose(fpr);
    return 1;
}


/**
* 計算一個檔案的大小
*/

# include <stdio.h>
# include <stdlib.h>

FILE * open_file(const char * filename, const char * mode);
long file_size(FILE * fp);

int main() {

    FILE * fpr = open_file("/Users/zhangqingli/Desktop/test.txt", "r");
    long length = file_size(fpr);
    printf("檔案的大小為:%ld byte\n", length);

    return 0;
}

//開啟檔案
FILE * open_file(const char * filename, const char * mode) {
    FILE * fp;

    fp = fopen(filename, mode);
    if (fp==NULL) {
        printf("檔案開啟失敗!\n");
        exit(1);
    }
    return fp;
}

//計算檔案大小
long file_size(FILE * fp) {
    long curpos, length;

    //計算檔案大小
    curpos = ftell(fp);
    fseek(fp, 0L, SEEK_END);
    length = ftell(fp);

    //恢復檔案指標到當前位置
    fseek(fp, curpos, SEEK_SET);

    return length;
}