C Gossip: 未格式化檔案 I/O

要處理檔案的輸出入,您必須先include <stdio.h>標頭,如果要處理檔案輸出,您要使用fopen()函式開啟檔案,fopen()函式的雛型宣告如下: 
FILE* fopen (const char*, const char*);

FILE是個
struct自訂型態:
typedef struct _iobuf
{
    char*    _ptr;
    int    _cnt;
    char*    _base;
    int    _flag;
    int    _file;
    int    _charbuf;
    int    _bufsiz;
    char*    _tmpfname;
} FILE;

fopen()會傳回一個FILE實例的位址值,實際上您不太需要了解FILE的每個成員作用,您只要將FILE的位址值傳給像是fgetc()、fputc()、fgets()、fputs()的函式進檔案I/O處理即可,fopen ()的第一個參數用來指定要開啟的檔案名稱,第二個參數用來指定檔案I/O模式,模式基本上就是讀、寫、附加,分別可使用r、w與a來設定,如果加上+, 表示檔案可讀可寫,如果加上b,表示以區塊(block)方式,即二進位方式進行讀寫,例如以下是可設定的模式:
r 開啟檔案進行唯讀,若檔案不存在,則傳回NULL
w 開啟檔案進行唯寫,若檔案不存在,則建立新檔,則檔案存在則將之刪除,再建立新檔
a 開啟檔案進行附加,若檔案存在,則資料從檔案尾端寫入,則檔案不存在則建立新檔
rb 以二進位模式開啟檔案進行唯讀,Windows 下需要加 b,Linux 下則會予以忽略
wb 以二進位模式開啟檔案進行唯寫,Windows 下需要加 b,Linux 下則會予以忽略
ab 以二進位模式開啟檔案進行附加,Windows 下需要加 b,Linux 下則會予以忽略
r+ 開啟檔案進行讀寫,若檔案不存在,則建立新檔,若檔案存在,資料將從檔案開頭進行覆寫
w+ 開啟檔案進行讀寫,若檔案不存在,則建立新檔,若檔案存在則覆寫原有的資料
a+ 開啟檔案進行附加、讀取,若檔案不存在則建立新檔,若檔案存在,則資料從檔案尾端寫入
r+b 以二進位方式開啟檔案進行讀寫,Windows 下需要加 b,Linux 下則會予以忽略
w+b 以二進位方式開啟檔案進行讀寫,Windows 下需要加 b,Linux 下則會予以忽略
a+b 以二進位方式開啟檔案進行附加、讀取,Windows 下需要加 b,Linux 下則會予以忽略

Windows作業系統將文字檔和二進位檔案當作兩種不同的檔案,而Linux則不區別,在Windows下讀寫非文字檔案,必須加上b模式,在Linux下則會忽略b。

例如以下的程式片段可開啟一個檔案進行讀取:
FILE *file = fopen("test.txt", "w");

若開啟檔案成功,則file將儲存位址值,您可以使用以下的程式片段來測試檔案是否開啟成功:
if(file == NULL) {
    puts("檔案開啟失敗");
}

NULL為使用#define定義的展開字,其值為0:
#define NULL 0

fopen()會使用緩衝區來減少對磁碟的實際I/O,以加快檔案存取效率,當您在程式中進行讀寫動作時,實際上會先對緩衝區作存取,而非實際的磁碟,檔案開啟一個重要的觀念與習慣是,不使用檔案時,一定要記得關閉檔案,閉關檔案會將緩衝區中的資料真正寫入磁碟,若忘了關閉檔案,可能會造成資料的遺失。

您可以使用fclose()來關閉檔案:
int fclose(FILE *fp);

若檔案正常關閉,則傳回0,否則將傳回非0值。

開啟檔案之後,您可以使用fgetc()來讀取檔案中的字元,使用fputc()來將字元寫入檔案:
int fgetc(FILE* fp);
int fputc(int ch, FILE *fp);

fgetc()傳入FILE實例的位址值,每執行一次就會從檔案中讀取一個字元,直到讀到檔尾(End of File, EOF)為止,文字模式時判斷檔案結尾,可以如下撰寫:
while((ch = fgetc(file)) != EOF) {
    ...
}

使用fgetc(),只要指定FILE位址值給它就可以了,而fputc()則指定要寫入的字元及FILE位址值。

下面這個程式直接示範如何讀取並寫入純文字檔案,會將指定的檔案讀取並複製至另一個檔案:
#include <stdio.h> 
                                                                                
int main(int argc, char* argv[]) {
    if(argc != 3) { 
        puts("指令: copy <來源檔案名稱> <目的檔案名稱>"); 
        return 1; 
    } 
                         
    FILE *file1 = fopen(argv[1], "r");                            
    if(!file1) { 
        puts("來源檔案開啟失敗"); 
        return 1; 
    }
    
    FILE *file2 = fopen(argv[2], "w");
    if(!file2) { 
        puts("目的檔案開啟失敗"); 
        return 1; 
    }    

    char ch;                                      
    while((ch = fgetc(file1)) != EOF) { 
        fputc(ch, file2);
    } 
          
    fclose(file1);
    fclose(file2);
        
    return 0; 
}

您也可以使用fgets()來讀取整個字串,使用fputs()來寫入整個字串:
char* fgets(char *str, int length, FILE *fp);
int fputs(char *str, FILE *fp);

fgets()第一個參數為要讀入的字串儲存的陣列位址,第二個參數為要讀入的字元長度,由於字串必須包留字元陣列最後一個元素為空白字元,才視之為字 串,所以實際讀入的長度為length - 1,第三個參數為FILE位址值,而fputs()第一個參數為寫入的字串,第二個參數為FILE位址值。

以下的程式使用fgets()、fputs()改寫上面這個程式:
#include <stdio.h> 
                                                                                
int main(int argc, char* argv[]) {
    if(argc != 3) { 
        puts("指令: copy <來源檔案名稱> <目的檔案名稱>"); 
        return 1; 
    } 
                         
    FILE *file1 = fopen(argv[1], "r");
    if(!file1) { 
        puts("來源檔案開啟失敗"); 
        return 1; 
    }
    
    FILE *file2 = fopen(argv[2], "w");
    if(!file2) { 
        puts("目的檔案開啟失敗"); 
        return 1; 
    }    

    char str[50];
    while(fgets(str, 50, file1) != NULL) { 
        fputs(str, file2);
    } 
          
    fclose(file1);
    fclose(file2);
        
    return 0; 
}

在程式執行過程開啟的標準輸出stdout、標準輸入stdin、標準錯誤stderr,事實上也是檔案串流的特例,在C程式中,也常見到以下的方式,以便直接控制這三個標準輸入、輸出、錯誤:
#include <stdio.h> 
                                                                                
int main(int argc, char* argv[]) {
    if(argc != 3) { 
        fputs("指令: copy <來源檔案名稱> <目的檔案名稱>", stderr); 
        return 1; 
    } 
                         
    FILE *file1 = fopen(argv[1], "r");
    if(!file1) { 
        fputs("來源檔案開啟失敗", stderr); 
        return 1; 
    }
    
    FILE *file2 = fopen(argv[2], "w");
    if(!file2) { 
        fputs("目的檔案開啟失敗", stderr); 
        return 1; 
    }    

    char str[50];
    while(fgets(str, 50, file1) != NULL) { 
        fputs(str, file2);
    } 
          
    fclose(file1);
    fclose(file2);
        
    return 0; 
}

程式的執行結果與上一個範例是相同的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章