HOWTO · C

C 言語で fscanf を使ってファイルを一行ずつ読む

この記事では、C 言語で fscanf を使ってファイルを一行ずつ読み込む方法を説明します。

このページの内容

fscanf は FILE ストリームから書式付き入力を読み取ります。スキャンセットを使うと改行までの文字を集められますが、専用の行読み込み関数とは異なり、空行やバッファに収まる長さのデータを明示的に扱う必要があります。一般的なテキストファイルでは fgets のほうが通常は簡単です。入力形式に応じた変換が必要な場合は fscanf を使います。

Cでfscanfを使ってファイルを1行ずつ読み取る

次の完全な例では、一時ファイルを作成してサンプルの2行を書き込み、格納先バッファのサイズである16バイトに収まるようスキャンセットでストリームを読み取ります。プログラムは一時ストリームに書き込んだ2行を、読み取りながら出力します。

#include <stdio.h>

int main(void) {
    FILE *file = tmpfile();
    char line[16];

    if (file == NULL) {
        return 1;
    }
    if (fputs("First line.\nSecond line.\n", file) == EOF || ferror(file)) {
        fclose(file);
        return 1;
    }
    if (fflush(file) == EOF || ferror(file)) {
        fclose(file);
        return 1;
    }
    rewind(file);

    for (;;) {
        int result = fscanf(file, "%15[^\n]", line);
        if (result == 1) {
            int ch;
            puts(line);
            ch = fgetc(file);
            if (ch == '\n') {
                continue;
            }
            if (ch == EOF) {
                break;
            }
            ungetc(ch, file);
            while ((ch = fgetc(file)) != '\n' && ch != EOF) {
            }
            if (ch == EOF) {
                break;
            }
            continue;
        }
        if (result == EOF) {
            break;
        }
        if (fgetc(file) == '\n') {
            puts("");
        }
    }

    fclose(file);
    return 0;
}

幅を15にすると、line の末尾に終端ヌル文字を格納する余地が残ります。スキャンセットによる変換に成功するたび、プログラムは改行文字を確認します。行がバッファより長い場合は、次の行を読む前に残りを破棄します。これによりメモリ使用量を一定に保てますが、長い行全体は保存されません。改行位置でスキャンセットに一致する文字がない場合、ループは改行を読み飛ばして空行を出力するため、進まないまま再試行することはありません。本番コードでは、入力終了後に ferror(file) も確認し、読み取りエラーと通常のファイル終端を区別してください。

fscanfで空白区切りのトークンを読み取る

トークンを解析する場合、%255s は空白以外の文字からなるトークンを1つ、char token[256] バッファに読み取ります。幅を指定しているため配列の範囲を超えて書き込まず、%s は先頭の空白を読み飛ばし、次の空白文字で読み取りを止めます。ループは fscanf(file, "%255s", token) == 1 の間だけ続けてください。EOF だけを確認すると、入力を消費しない変換失敗が起きたときにループが終わらず、繰り返し続けることがあります。この方法ではスペース、タブ、改行を区切りとして扱うため、元の行の境界は保持されません。数値フィールドには対応する変換指定子を使い、代入に成功した数も確認してください。

行単位で確実に読み取るには fgets を使う

各レコードが1行であるテキストでは、fgets(buffer, sizeof buffer, file) のほうが一般に動作を把握しやすくなります。バッファ容量より最大1文字少ない長さまで読み取り、収まる場合は改行も保持します。バッファを使う前に、戻り値を確認してください。改行が入る前にバッファがいっぱいになった場合、論理上の行はストリーム内で続いています。用途に応じて、さらに読み取るか、残りを破棄してください。最後の行には改行がない場合があります。空行も自然に扱え、改行だけを含むバッファが返ることがあります。

書式付きフィールドや空白区切りのトークンが必要なら fscanf を使います。行の境界を保持したい場合は fgets を使い、必要に応じて読み取った文字列を別途解析してください。fopen の成功、変換結果を必ず確認し、使用後はストリームを閉じてください。