檔案的輸出入定義在 stdio.h 標頭檔,若要開啟檔案,可以使用 fopen
,其函式原型宣告如下:
FILE* fopen( const char* filename, const char* mode )
FILE
是個結構型態:
typedef struct _iobuf {
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
} FILE;
fopen
傳回 FILE
實例的位址,若將 FILE
的位址傳給 fgetc
、fputc
、fgets
、fputs
等函式,字元串流只是在讀取或寫入的過程,會進行文字編碼的轉換,例如 int
數字 9,在寫入的操作中,會轉換為編碼 57 的位元組資料,至於本身是 char
的資料,就直接以對應的位元組寫出,也就是所謂的純文字檔案讀寫。
fopen
第一個參數用來指定檔案名稱,第二個參數用來指定輸出入模式,模式基本上就是讀、寫、擴充等,分別可使用 r
、w
與 a
等設定,若加上 +
, 表示擴充檔案讀寫能力。例如,以下是可設定的模式:
r
:唯讀模式開啟檔案,若檔案不存在,傳回NULL
。w
:唯寫模式建立檔案,若檔案不存在,建立新檔,若檔案存在,就刪除其內容。a
:附加模式開啟檔案,若檔案不存在,建立新檔,若檔案存在,從檔案尾端寫入。r+
:開啟檔案進行讀寫,若檔案不存在,傳回NULL
,若檔案存在,從檔案開頭進行讀寫。w+
:建立檔案進行讀寫,若檔案不存在,建立新檔,若檔案存在,就刪除其內容。a+
:開啟檔案進行讀寫,若檔案不存在,建立新檔,若檔案存在,從檔案尾端寫入。
可以選擇性地加上 b
表示以二進位模式,在 POSIX 系統上會忽略,Windows 會不處理 \n
與 \x1A
。
例如,以下可開啟一個檔案進行讀取:
FILE *fp = fopen("test.txt", "r");
可以使用以下片段來測試檔案是否開啟成功:
if(!fp) {
perror("檔案開啟失敗"); // 將訊息輸出至 stderr
return EXIT_FAILURE;
}
開啟檔案之後,可以使用 fgetc
來讀取檔案中的字元,使用 fputc
來將字元寫入檔案,以讀取為例:
int c;
while ((c = fgetc(fp)) != EOF) {
putchar(c);
}
ferror
可以檢查檔案讀寫是否有誤,feof
可檢查讀取到檔尾:
if (ferror(fp)) {
puts("讀取時發生錯誤");
}
else if (feof(fp)) {
puts("讀取成功");
}
fopen
會使用緩衝區減少對磁碟的讀寫,不使用檔案時,記得關閉檔案,關閉檔案會將緩衝區中的資料寫入磁碟,若忘了關閉檔案,可能會造成資料遺失,可以使用 fclose
來關閉檔案:
fclose(fp);
下面這個程式示範如何讀取來源檔案並將內容寫為另一檔案:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
FILE* src = fopen(argv[1], "r");
if(!src) {
perror("無法開啟來源檔案");
return EXIT_FAILURE;
}
FILE* dest = fopen(argv[2], "w");
if(!dest) {
perror("無法建立目標檔案");
return EXIT_FAILURE;
}
int c;
while ((c = fgetc(src)) != EOF) {
fputc(c, dest);
}
if (ferror(src) || ferror(dest)) {
puts("讀寫時發生錯誤");
}
fclose(src);
fclose(dest);
}
也可以使用 fgets
來讀取整個字串,使用 fputs
來寫入整個字串:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
FILE* src = fopen(argv[1], "r");
if(!src) {
perror("無法開啟來源檔案");
return EXIT_FAILURE;
}
FILE* dest = fopen(argv[2], "w");
if(!dest) {
perror("無法建立目標檔案");
return EXIT_FAILURE;
}
char buf[8];
while (fgets(buf, sizeof(buf), src) != NULL) {
fputs(buf, dest);
}
if (ferror(src) || ferror(dest)) {
perror("讀寫時發生錯誤");
}
fclose(src);
fclose(dest);
}
fgets
第一個參數為用來儲存讀入的資料,第二個參數為讀入的字元長度,第三個參數為 FILE
位址值,而 fputs
第一個參數為寫入的資料,第二個參數為 FILE
位址值。
以下的程式使用 fgets
、fputs
改寫以上範例:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
FILE* src = fopen(argv[1], "r");
if(!src) {
perror("無法開啟來源檔案");
return EXIT_FAILURE;
}
FILE* dest = fopen(argv[2], "w");
if(!dest) {
perror("無法建立目標檔案");
return EXIT_FAILURE;
}
char buf[8];
while (fgets(buf, sizeof(buf), src) != NULL) {
fputs(buf, dest);
}
if (ferror(src) || ferror(dest)) {
perror("讀寫時發生錯誤");
}
fclose(src);
fclose(dest);
}
在程式執行過程開啟的標準輸出 stdout
、標準輸入 stdin
、標準錯誤 stderr
,事實上也是檔案的特例,在 C 程式中,也常見到以下的方式,以便直接控制這三個標準輸入、輸出、錯誤:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
FILE* src = fopen(argv[1], "r");
if(!src) {
fputs("無法開啟來源檔案", stderr);
return EXIT_FAILURE;
}
FILE* dest = fopen(argv[2], "w");
if(!dest) {
fputs("無法建立目標檔案", stderr);
return EXIT_FAILURE;
}
char buf[8];
while (fgets(buf, sizeof(buf), src) != NULL) {
fputs(buf, dest);
}
if (ferror(src) || ferror(dest)) {
fputs("讀寫時發生錯誤", stderr);
}
fclose(src);
fclose(dest);
}