【新手必看】C語言檔案操作完全掌握!fopen、fread、fprintf 實例教學

目次

1. C語言中的檔案操作基礎是什麼?

檔案操作是程式與「外部」互動的手段

C語雖然是偏向系統的低階語言,但也在標準函式庫中提供實用的檔案輸入輸出功能。檔案操作指的是程式對電腦內的檔案(文字檔或二進位檔)進行讀寫的處理,以下情況會需要它。
  • 資料的永續化(程式結束後仍保存資料)
  • 記錄日誌(追蹤執行內容與錯誤)
  • 從外部檔案讀取設定(可彈性變更設定)
如此一來,檔案操作成為實用的 C 語程式不可或缺的技術之一。

文字檔與二進位檔的差異

在 C 語中,主要可處理以下兩種檔案類型。
  • 文字檔(.txt 等) 由人類可讀的文字資訊構成的檔案。主要使用 fprintffscanf 等操作。
  • 二進位檔(.dat、.bin 等) 不是以文字形式可讀,而是直接儲存機器用資訊的檔案。使用 fwritefread 處理。
對於初學者而言,多數情況會處理文字檔,但在處理影像資料或結構體資料等情況下,二進位檔的知識也變得重要。

檔案操作的基本流程

在 C 語中執行檔案操作時,基本流程如下。
  1. 開啟檔案:使用 fopen 函式開啟檔案,取得檔案指標。
  2. 對檔案進行讀寫:使用 fprintffscanffreadfwrite 等函式進行資料交換。
  3. 關閉檔案:使用 fclose 函式關閉檔案,釋放資源。
FILE *fp;
fp = fopen("sample.txt", "r");
if (fp != NULL) {
    // 讀取處理等
    fclose(fp);
}
只要了解這樣的結構,就能應用於各種檔案操作。

總結:理解 C 語檔案操作的第一步

C 語的檔案操作不僅是單純的輸入輸出處理,更是與實際應用程式之間的橋樑。掌握檔案類型與基本處理流程後,之後學習的進階技巧(如日誌輸出、CSV 操作、設定檔的活用)也會更容易理解。

2. 開啟與關閉檔案的方法(fopen / fclose)

檔案操作的起點是「fopen」

C 語言在操作檔案時,首先需要的是 fopen 函式。它會開啟特定檔案,並回傳用於操作該檔案的「檔案指標(FILE 型)」。
FILE *fp;
fp = fopen("example.txt", "r");
在此範例中,以唯讀(r)模式開啟名為「example.txt」的檔案。成功時,fp 會被指派檔案指標;失敗時則回傳 NULL

模式指定:依需求選擇開啟方式

fopen 需要在第二個參數指定「模式」。這是表示檔案如何處理的字串,種類如下:
模式說明
“r”以唯讀方式開啟(檔案不存在時失敗)
“w”以寫入方式開啟(若檔案已存在則覆寫,若不存在則新建)
“a”以追加方式開啟(若檔案不存在則新建)
“rb”/”wb”/”ab”以二進位模式開啟(主要在 Windows 使用)
“r+”以讀寫模式開啟(不會覆寫)
“w+”以讀寫模式開啟(總是覆寫)
“a+”以讀寫模式開啟(只能追加)

一定要檢查 fopen 的返回值

在開啟檔案的處理中,基本上要確認 fopen 的返回值是否為 NULL。失敗的原因多種多樣,主要包括以下:
  • 檔案不存在(尤其是 “r” 模式)
  • 沒有檔案的存取權限
  • 檔案路徑錯誤
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
    perror("無法開啟檔案");
    return 1;
}

處理結束後一定要用「fclose」關閉

在操作檔案後,需要使用 fclose 函式關閉已開啟的檔案。如果不關閉,會導致記憶體洩漏或檔案損毀。
fclose(fp);
這也意味著「釋放資源」,在同時開啟多個檔案的情況下尤為重要。

fopen、fclose 的總結與最佳實踐

  • 不要忘記檢查 fopen 是否為 NULL 使用 perror 可以更容易了解原因。
  • 遵守「開啟檔案後即關閉」的原則 使用完畢的檔案務必關閉。returnexit 前也別忘了關閉。

實作範例:以追加模式開啟日誌檔案

FILE *log_fp = fopen("log.txt", "a");
if (log_fp == NULL) {
    perror("無法開啟日誌檔案");
    return 1;
}

fprintf(log_fp, "程式已啟動\n");

fclose(log_fp);
這樣,以 a 模式開啟即可輕鬆追加日誌。每次不會覆寫而是新增記錄,最適合日誌用途。
侍エンジニア塾

3. 寫入檔案的方法(fprintf·fputs·fwrite)

C語言的檔案寫入分為「格式」與「二進位」

在 C 語言中寫入檔案資料時,需依目的使用三個不同的函式。
  • fprintf:依格式指定的文字輸出(與 printf 類似的語法)
  • fputs:直接將字串輸出為文字
  • fwrite:用於寫入結構體或二進位資料
了解各自的特性並適當區分使用,即可有效實作資料的儲存處理。

fprintf 函式:以 printf 感覺寫入檔案

FILE *fp = fopen("output.txt", "w");
if (fp != NULL) {
    fprintf(fp, "姓名: %sn", "佐藤");
    fprintf(fp, "年齡: %dn", 28);
    fclose(fp);
}
輸出結果:
姓名: 佐藤
年齡: 28

fputs 函式:適合逐行文字輸出

FILE *fp = fopen("log.txt", "a");
if (fp != NULL) {
    fputs("應用程式已啟動n", fp);
    fputs("使用者已登入n", fp);
    fclose(fp);
}

fwrite 函式:用於二進位檔案或結構體儲存

struct User {
    int id;
    char name[20];
};

struct User user = {101, "Yamada"};

FILE *fp = fopen("user.dat", "wb");
if (fp != NULL) {
    fwrite(&user, sizeof(struct User), 1, fp);
    fclose(fp);
}

了解 fwrite 與 fprintf 的差異

特性fprintf / fputsfwrite
形式文字二進位
是否可供人類閱讀×(基本上不可讀)
結構體輸出不適合擅長
換行處理需要明確指定不需要(因為是二進位)

檔案寫入時的注意事項

  • 寫入後務必使用 fclose 關閉檔案
  • 若需立即將資料寫入檔案,請使用 fflush(fp);
  • 寫入失敗時可使用 perrorferror 進行錯誤處理

實用範例:將感測器資料寫入 CSV 檔案

FILE *fp = fopen("sensor.csv", "w");
if (fp != NULL) {
    fprintf(fp, "溫度,濕度n");
    fprintf(fp, "%.1f,%.1fn", 24.3, 60.2);
    fprintf(fp, "%.1f,%.1fn", 25.1, 58.7);
    fclose(fp);
}

4. 從檔案讀取的方法(fscanf・fgets・fread)

讀取時以「符合目的的方法」為要訣

C 語言在從檔案讀取資料時,也和寫入一樣,使用的函式會依文字格式或二進位格式而不同。
  • fscanf:從文字檔以格式化方式讀取
  • fgets:逐行以字串方式讀取
  • fread:從二進位檔讀取資料

fscanf 函式:指定格式讀取數值與字串

FILE *fp = fopen("data.txt", "r");
if (fp != NULL) {
    char name[50];
    int age;
    fscanf(fp, "%s %d", name, &age);
    printf("姓名: %s, 年齡: %dn", name, age);
    fclose(fp);
}

fgets 函式:最適於逐行讀取

char buffer[256];
FILE *fp = fopen("log.txt", "r");
if (fp != NULL) {
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("%s", buffer);
    }
    fclose(fp);
}

fread 函式:一次性讀取二進位資料或結構體

struct User {
    int id;
    char name[20];
};

struct User user;

FILE *fp = fopen("user.dat", "rb");
if (fp != NULL) {
    fread(&user, sizeof(struct User), 1, fp);
    printf("ID: %d, 姓名: %sn", user.id, user.name);
    fclose(fp);
}

讀取處理時常見的注意事項

  • 檔案結尾的偵測可透過 feof()fgets/fscanf 的返回值確認
  • 換行與空白的處理若未妥善處理,資料會錯位
  • 錯誤時的處理可考慮使用 ferror()perror()

實用範例:逐行讀取與解析 CSV 檔案

char line[100];
FILE *fp = fopen("sensor.csv", "r");
if (fp != NULL) {
    while (fgets(line, sizeof(line), fp)) {
        char temp[10], humid[10];
        sscanf(line, "%[^,],%s", temp, humid);
        printf("溫度: %s℃ 濕度: %s%%n", temp, humid);
    }
    fclose(fp);
}

總結:依目的適當使用 fscanf・fgets・fread

函式名稱適用用途格式特點
fscanf數值與字串的讀取需要固定的格式對格式敏感
fgets逐行字串讀取格式自由包含換行
fread二進位資料與結構體不限制格式與寫出相對應

5. 檔案操作時的錯誤處理與除錯

為什麼錯誤處理很重要?

C 語言的檔案操作中,可能失敗的處理非常多,這是其特點。例如可以考慮以下情況:
  • 檔案不存在
  • 沒有讀取權限
  • 磁碟空間不足導致寫入失敗
  • 因格式錯誤而導致讀取錯誤

fopen 失敗時會回傳 NULL

FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
    perror("無法開啟檔案");
    return 1;
}

perror 函式:直觀的錯誤訊息顯示

perror("發生錯誤");
執行結果範例:
發生錯誤: No such file or directory

strerror 函式:取得錯誤訊息字串

#include <string.h>
#include <errno.h>

fprintf(stderr, "錯誤內容: %sn", strerror(errno));

ferror 函式:檢查輸入輸出串流是否發生錯誤

if (ferror(fp)) {
    fprintf(stderr, "檔案操作中發生錯誤n");
}

feof 函式:確認是否已達檔案結尾

while (!feof(fp)) {
    fgets(buffer, sizeof(buffer), fp);
    // ...
}
較安全的方法:
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
    // 讀取處理
}

常見的檔案操作錯誤與其原因

症狀主要原因對策
fopen 回傳 NULL檔案不存在、權限不足、路徑錯誤使用 perror 確認詳細
寫入後未反映因緩衝導致未寫入使用 fflush 或 fclose
資料損壞格式不一致、二進位讀寫錯誤確認結構體大小與順序
讀取途中中止換行或 EOF 處理錯誤使用 fgets 或回傳值控制

總結:安全的檔案操作需要穩健的檢查

  • fopen 的回傳值檢查(NULL 判斷)
  • perrorstrerror 的原因說明
  • ferrorfeof 的狀態檢查
結合這些,實作安全且高可靠性的檔案處理吧。

6. 實作範例|使用 C 語言檔案操作的程式

不僅理論,還要透過實際的使用方式加深理解

ここまでで、C言語によるファイル操作の基本から応用テクニックまで解説してきました。このセクションでは、実際に役立つファイル操作のサンプルコードを通じて、学んだ知識を「使える形」に落とし込んでいきます。

寫入日誌檔案:記錄應用程式的運作

#include <stdio.h>
#include <time.h>

void write_log(const char *message) {
    FILE *fp = fopen("app.log", "a");
    if (fp != NULL) {
        time_t now = time(NULL);
        fprintf(fp, "[%s] %sn", ctime(&now), message);
        fclose(fp);
    }
}
int main() {
    write_log("應用程式已啟動");
    write_log("處理已正常完成");
    return 0;
}

CSV檔案的資料寫入:表格式的資料保存

#include <stdio.h>

int main() {
    FILE *fp = fopen("data.csv", "w");
    if (fp != NULL) {
        fprintf(fp, "ID,姓名,分數n");
        fprintf(fp, "1,田中,88n");
        fprintf(fp, "2,山田,92n");
        fclose(fp);
    }
    return 0;
}

讀取設定檔(.conf):彈性的設定管理

config.conf:
username=guest
timeout=30
#include <stdio.h>
#include <string.h>

int main() {
    FILE *fp = fopen("config.conf", "r");
    char line[100];
    if (fp != NULL) {
        while (fgets(line, sizeof(line), fp)) {
            char key[50], value[50];
            if (sscanf(line, "%[^=]=%s", key, value) == 2) {
                printf("設定項目: %s, 值: %sn", key, value);
            }
        }
        fclose(fp);
    }
    return 0;
}

多筆資料的結構體二進位保存範例

#include <stdio.h>

struct User {
    int id;
    char name[20];
};

int main() {
    struct User users[2] = {
        {1, "Suzuki"},
        {2, "Kato"}
    };

    FILE *fp = fopen("users.dat", "wb");
    if (fp != NULL) {
        fwrite(users, sizeof(struct User), 2, fp);
        fclose(fp);
    }
    return 0;
}

總結:檔案操作是可在各種情境中使用的利器

  • 日誌:用於操作確認與錯誤追蹤
  • CSV:用於與外部工具的資料連接
  • 設定檔:供使用者進行外部操作
  • 二進位保存:用於高速且省容量的資料處理

7. C 語言檔案操作相關的常見問題(FAQ)

一次解決新手容易卡住的重點!

在此,我們以問答形式彙整了常見的疑問與實務中常遇到的問題。

Q1. 為什麼使用 fopen 無法開啟檔案?

A1. 可能的原因如下:
  • 檔案不存在
  • 路徑錯誤
  • 沒有存取權限
  • 檔案被鎖定
處理方法:
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
    perror("無法開啟檔案");
}

Q2. 為什麼寫入的內容沒有反映到檔案?

A2. 可能因為緩衝機制尚未立即寫入。 處理方法:
fprintf(fp, "測試資料n");
fflush(fp);  // 或 fclose(fp);

Q3. 二進位檔案與文字檔案的差異是?

項目文字檔案二進位檔案
內容可讀的字串不可讀的資料
大小可能較大較小且高速
使用函式fprintf, fputsfwrite

Q4. 為什麼在檔案中途讀取會停止?

A4. 可能是 EOF 處理方式或讀取條件有問題。 安全的做法:
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
    // 處理
}

Q5. 文字編碼差異會導致文字亂碼嗎?

A5. 是的。因為 Shift_JIS 與 UTF-8 等差異,可能在不同作業系統間產生文字亂碼。 對策:
  • 在編輯器或編譯選項中明確統一字元編碼

Q6. 使用 fopen 同時開啟同一檔案多次可以嗎?

A6. 若為唯讀模式則可以,但若包含寫入則需注意。也需要考慮鎖定機制等。

Q7. 想把錯誤寫入日誌檔案而不是標準輸出時該怎麼做?

A7.
FILE *log = fopen("error.log", "a");
if (log != NULL) {
    fprintf(log, "發生錯誤: %sn", strerror(errno));
    fclose(log);
}

透過 FAQ 解決卡關,讓學習更扎實

此處介紹的 FAQ 收集了從初學者到中級者在 C 語言檔案處理時實際容易卡住的重點。

8. 總結|掌握 C 語言的檔案輸入輸出,擴展程式的範圍

回顧 C 語言的檔案操作

本文系統性地解說了 C 語言中檔案操作,從基礎到應用。具體而言,學習了以下內容。
  • 檔案類型(文字/二進位)與特性
  • 使用 fopen・fclose 進行檔案的開啟與關閉
  • 使用 fprintf・fputs・fwrite 的寫入方式
  • 使用 fscanf・fgets・fread 的讀取方式
  • 錯誤處理的基本與問題對策
  • 日誌輸出、CSV 保存、設定檔讀取等實用範例
  • 彙整新手容易卡關的要點 FAQ

檔案操作是與現實世界的接點

檔案輸入輸出不僅是程式內部的處理,更是與使用者及其他系統的「接點」。在日誌記錄、設定讀取、資料保存等各種實用用途上,都是不可或缺的功能。

作為未來的學習步驟

掌握此內容的人,建議再挑戰以下進階主題。
  • 結構體陣列或列表結構的檔案保存
  • 文字處理的高速化(緩衝區大小的最佳化等)
  • 檔案鎖定處理與排他控制的實作
  • 與壓縮檔(gz, zip)的整合(使用外部函式庫)
透徹掌握 C 語言的檔案操作,您的程式將進化為「實用的應用程式」。請務必以本文為參考,積極進行實作與驗證。