C語言讀取TXT檔案教學:詳解三種核心方法 (fgets, fgetc, fscanf) 與完整範例

前置準備

在執行程式之前,請先在您的專案目錄(或與您的 .c 檔案相同的資料夾)中建立一個名為 data.txt 的文字檔。

data.txt 檔案內容範例:codeTxt

Hello, this is the first line.
Welcome to C file reading.
This is the third and final line.

方法一:逐行讀取 (最常用且推薦)

這種方法使用 fgets() 函數,一次讀取檔案中的一整行,非常適合處理典型的文字檔。

程式碼範例

codeC

#include <stdio.h>
#include <stdlib.h> // 為了使用 exit()

#define MAX_LINE_LENGTH 256 // 定義每行最大長度

int main() {
    // 1. 宣告一個檔案指標
    FILE *fptr;
    char line[MAX_LINE_LENGTH]; // 用來儲存讀取到的每一行內容

    // 2. 開啟檔案
    // "r" 表示 "read" (讀取模式)
    fptr = fopen("data.txt", "r");

    // 3. 檢查檔案是否成功開啟
    if (fptr == NULL) {
        perror("Error opening file"); // perror 會印出錯誤訊息,例如 "No such file or directory"
        return 1; // 回傳非 0 值表示程式出錯
    }

    printf("檔案內容:\n");
    printf("------------------------------------\n");

    // 4. 迴圈讀取檔案的每一行
    // fgets 會讀取一行,包含換行符號 '\n',直到檔案結尾 (EOF) 或發生錯誤
    // 當 fgets 成功讀取時,會回傳 line 的指標 (非 NULL);到達檔案結尾時,回傳 NULL
    while (fgets(line, sizeof(line), fptr) != NULL) {
        // 5. 處理讀取到的每一行 (這裡我們直接印出來)
        printf("%s", line);
    }

    // 6. 關閉檔案
    // 這是一個非常重要的步驟,可以釋放系統資源
    fclose(fptr);

    return 0; // 程式正常結束
}

程式碼解說

  1. #include <stdio.h>:引入標準輸入輸出函式庫,其中包含了所有檔案操作所需的函數,如 fopen, fgets, fclose, printf 等。
  2. FILE *fptr;:宣告一個名為 fptr 的檔案指標 (File Pointer)。這個指標將會指向我們所開啟的檔案,後續所有對該檔案的操作都會透過這個指標來進行。你可以把它想像成是檔案在記憶體中的一個「代號」或「書籤」。
  3. fopen(“data.txt”, “r”);
    • fopen 是 “File Open” 的縮寫,用來開啟一個檔案。
    • 第一個參數是檔案名稱 (“data.txt”)。
    • 第二個參數是開啟模式。”r” 代表 read (唯讀模式)。如果檔案不存在,fopen 在 “r” 模式下會失敗並回傳 NULL。
    • 常見模式還有:”w” (write, 寫入)、”a” (append, 附加)。
  4. 錯誤處理 if (fptr == NULL)
    • 這是極其重要的步驟。如果 fopen 因為找不到檔案、沒有讀取權限等原因而失敗,它會回傳 NULL。
    • 如果沒有這個檢查就直接使用 fptr,程式會因為操作一個無效的指標而崩潰 (Segment Fault)。
    • perror(“自訂訊息”) 是一個好用的函數,它會印出你給的訊息,後面再附上系統提供的具體錯誤原因。
  5. while (fgets(line, sizeof(line), fptr) != NULL)
    • 這是整個讀取過程的核心。
    • fgets() 函數有三個參數:
      1. line:一個字元陣列(緩衝區),用來存放讀取到的內容。
      2. sizeof(line) 或 MAX_LINE_LENGTH:緩衝區的大小。fgets 最多只會讀取 size – 1 個字元,以確保最後能放入一個字串結束符 \0,這讓它比舊的 gets() 函數安全得多,可以有效防止緩衝區溢位。
      3. fptr:要從哪個檔案讀取。
    • fgets 會讀取一行文字(直到遇到換行符 \n 或檔案結尾 EOF)。它會連同換行符 \n 一起讀進來
    • 當成功讀取一行時,它會回傳指向 line 的指標;當讀到檔案結尾或發生錯誤時,它會回傳 NULL。因此,這個 while 迴圈會在讀完所有行後自動停止。
  6. printf(“%s”, line);:將剛剛讀取到緩衝區 line 裡的內容印出來。因為 fgets 已經包含了換行符,所以 printf 格式字串用 “%s” 就好,不需要額外加 \n。
  7. fclose(fptr);
    • 關閉檔案。這會將檔案緩衝區的資料寫回磁碟(如果是寫入模式),並釋放作業系統分配給這個檔案的資源。
    • 忘記關閉檔案是一個常見的錯誤,可能會導致資料遺失或資源洩漏。

方法二:逐字元讀取

這種方法使用 fgetc() 函數,一次只讀取一個字元。適合需要精確控制到每個字元的場景,例如計算字數或分析特定字元。

程式碼範例

codeC

#include <stdio.h>

int main() {
    FILE *fptr;
    int ch; // 注意!這裡用 int 而不是 char

    fptr = fopen("data.txt", "r");

    if (fptr == NULL) {
        perror("Error opening file");
        return 1;
    }

    printf("檔案內容 (逐字元):\n");
    printf("------------------------------------\n");

    // fgetc() 一次讀取一個字元
    // 當它讀到檔案結尾時,會回傳一個特殊值 EOF (End Of File)
    while ((ch = fgetc(fptr)) != EOF) {
        putchar(ch); // putchar() 是專門用來印出單一字元的函數
    }

    fclose(fptr);

    return 0;
}

程式碼解說

  • int ch;:為什麼用 int 而不是 char?因為 fgetc() 的回傳值有兩種可能:一個是 0 到 255 之間的字元值,另一個是代表檔案結尾的特殊常數 EOF (End Of File)。EOF 的值通常是 -1,超出了 char 型別的表示範圍。因此必須使用 int 來儲存回傳值,才能正確判斷是否已到達檔案結尾。
  • while ((ch = fgetc(fptr)) != EOF):這是一個經典的C語言寫法。
    1. ch = fgetc(fptr):執行 fgetc,讀取一個字元,並將其值賦予 ch。
    2. (…) != EOF:判斷賦值後的 ch 是否等於 EOF。如果不等於,就繼續迴圈。
  • putchar(ch);:將讀取到的字元 ch 印到螢幕上。它的效果和 printf(“%c”, ch); 相同。

方法三:格式化讀取

當你的文字檔有固定格式時(例如,每行都是 “姓名 年齡 分數”),可以使用 fscanf() 函數,它能像 scanf() 一樣直接解析並讀取特定型別的資料。

準備檔案 students.txt

codeTxt

Alice 20 85.5
Bob 22 92.0
Charlie 21 78.5

程式碼範例

codeC

#include <stdio.h>

int main() {
    FILE *fptr;
    char name[50];
    int age;
    float score;

    fptr = fopen("students.txt", "r");

    if (fptr == NULL) {
        perror("Error opening file");
        return 1;
    }

    printf("學生資料:\n");
    printf("Name\tAge\tScore\n");
    printf("---------------------\n");

    // fscanf 會根據格式字串 "%s %d %f" 來讀取和解析資料
    // 它會回傳成功匹配並賦值的項目數量
    // 在這裡,我們期望每次都能成功讀取 3 個項目
    while (fscanf(fptr, "%s %d %f", name, &age, &score) == 3) {
        printf("%s\t%d\t%.1f\n", name, age, score);
    }

    fclose(fptr);

    return 0;
}

程式碼解說

  • fscanf(fptr, “%s %d %f”, name, &age, &score)
    • 第一個參數 fptr 指明要從哪個檔案讀取。
    • 第二個參數是格式控制字串,告訴 fscanf 資料的格式(一個字串、一個整數、一個浮點數,用空白隔開)。
    • 後續參數是變數的地址,用來存放讀取到的值。
  • while (…) == 3:fscanf 的回傳值是它成功讀取並賦值的項目數量。在我們的例子中,如果它成功讀取了姓名、年齡和分數,就會回傳 3。當它讀到檔案結尾,或格式不匹配時,回傳值將不再是 3,迴圈便會停止。這是使用 fscanf 讀取整個檔案的標準且安全的方法。

三種方法比較總結

方法函數讀取單位適用場景優點缺點
逐行讀取fgets()一整行一般文字檔、日誌檔、設定檔簡單、安全(防止緩衝區溢位)、效率高需要自己解析行內資料
逐字元讀取fgetc()單一字元計算字數、詞頻,或進行精細的字元級剖析控制粒度最細對於大檔案,效率可能較低
格式化讀取fscanf()特定格式的資料格式固定的資料檔 (如CSV)能直接將文字轉換為變數,方便處理格式稍有錯誤就可能導致讀取失敗,不夠彈性

對於初學者和絕大多數應用場景,強烈建議優先使用 fgets() 進行逐行讀取,因為它最通用、也最安全。

Similar Posts