C言語基礎|ファイルの中身の表示

「ファイルは“読む”だけで価値が出る。まずは中身をそのまま表示してみよう!」

キーボードから入力された文字を画面にそのまま出す、というプログラムは「入出力の基本」を学ぶのにちょうど良い練習でした。
でも、入力元をキーボードからファイルに変えるだけで、一気に実用感が出ます。

たとえば、設定ファイルの確認、ログの中身の表示、テキストの簡単な閲覧など、「ファイルの内容を表示する」だけで役立つ場面はたくさんあります。
この節では、ファイルを開いて、1文字ずつ読み取り、画面に出すという王道パターンを、丁寧に理解していきます。

何を作る?:ファイルを表示する最小の実用プログラム

ここでは、元の例(ファイル名を入力して全部表示)を少し変更して、よりシンプルにします。

  • ファイル名はコマンドライン引数で受け取る(入力処理を簡単にする)
  • 表示メッセージは英語にする
  • コメントは日本語のまま
  • 中身はそのまま標準出力に流す

サンプルプログラム

プロジェクト名:chap13-8-1 ソースファイル名:chap13-8-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

Windows の Visual Studio が実行環境の場合、以下のパスにファイルを保存します。
C:\Users\<ユーザー名>\source\repos\chap13-8-1\chap13-8-1

#include <stdio.h>

int main(int argc, char *argv[])
{
    /* 引数チェック:ファイル名が指定されているか */
    if (argc != 2) {
        printf("Usage: showfile <filename>\n");
        return 1;
    }

    /* ファイルを読み取りモードで開く */
    FILE *fp = fopen(argv[1], "r");
    if (fp == NULL) {
        printf("ERROR: Could not open the file.\n");
        return 1;
    }

    printf("---- File content start ----\n");

    /* 1文字ずつ読み取って、そのまま出力する */
    int ch;
    while ((ch = fgetc(fp)) != EOF) {
        putchar(ch);
    }

    printf("\n---- File content end ----\n");

    /* ファイルを閉じる */
    fclose(fp);
    return 0;
}

実行方法

コマンドライン引数を指定してプログラムを実行・デバッグするには、プロジェクトのプロパティ設定で引数を指定します。

1.プロパティ画面の左側のメニューから「構成プロパティ」→「デバッグ」を選択します。
2.右側の画面にある「コマンド引数」(またはCommand-line Arguments)の項目に、渡したい引数を入力します。

実行イメージ

たとえば、sample.txt が次の内容だったとします。

Hello
C file I/O!

実行するとこうなります。

---- File content start ----
Hello
C file I/O!

---- File content end ----

全体の流れを図でつかむ

図:ファイル → ストリーム → 画面

[ファイル sample.txt]
        ↓ fopen
[入力ストリーム fp]
        ↓ fgetc で1文字ずつ取り出す
[文字 ch(int)]
        ↓ putchar
[画面(stdout)]

図の説明

  • fopen でファイルを開くと、ファイルに結び付いたストリーム fp ができます。
  • fgetc(fp) がストリームから「次の1文字」を取り出します。
  • 取り出した文字を putchar で画面に出せば、ファイルの中身が表示できます。

getchar と fgetc の違いがポイント

もともとの練習では getchar を使って「標準入力から1文字ずつ」取り出していました。
ファイルに変えるなら、同じ役割の関数が fgetc です。

getchar と fgetc の対応

関数どこから読む?引数使いどころ
getchar標準入力 stdinなしキーボード入力を読む
fgetc任意の入力ストリームstreamファイルなどから読む

表の説明

  • getchar は入力元が stdin に固定された fgetc だと考えるとスッキリします。
  • fgetc(fp) の fp を stdin にすれば、実質 getchar と同じ流れになります。

fgetc の書式と戻り値(ここは超重要)

fgetc の書式

項目内容
ヘッダ#include <stdio.h>
形式int fgetc(FILE *stream);
役割stream から次の1文字を読む
返り値読んだ文字(int)または EOF

表の説明

  • 返り値が int なのは、文字(0〜255)だけでなく EOF(通常 -1)も表したいからです。
  • そのため、受け取る変数 ch は char ではなく int が基本です。

1文字ずつ読む while の意味

この部分が “ファイル表示の心臓” です。

図:while の判定

ch = fgetc(fp)   → 文字なら ch に入る
ch が EOF でない → ループ継続
ch が EOF        → ループ終了(ファイル終端 or 読み取りエラー)

図の説明

  • ファイルの終わりに到達すると fgetc は EOF を返します。
  • それを合図にループを抜けるので、「最後まで読む」処理が自然に書けます。

登場する命令(関数)の書式まとめ

fopen

項目内容
ヘッダ#include <stdio.h>
形式FILE *fopen(const char *filename, const char *mode);
fopen(argv[1], "r")
失敗NULL

fgetc

項目内容
ヘッダ#include <stdio.h>
形式int fgetc(FILE *stream);
ch = fgetc(fp)
終了条件EOF

putchar

項目内容
ヘッダ#include <stdio.h>
形式int putchar(int c);
putchar(ch)
役割1文字出力

fclose

項目内容
ヘッダ#include <stdio.h>
形式int fclose(FILE *stream);
役割ストリームを閉じる

表の説明

  • ファイル入力の基本セットは fopen → fgetc → putchar → fclose です。
  • まずこの流れが書けるようになると、次に「行数カウント」「文字種の集計」などへ自然に進めます。

演習問題

演習13-6:行数(改行数)を数える

ファイル名を指定して、そのファイル中の行数(改行文字の個数)を数えて表示せよ。

解答例

プロジェクト名:chap13-8-2 ソースファイル名:chap13-8-2.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

int main(int argc, char *argv[])
{
    if (argc != 2) {
        printf("Usage: countlines <filename>\n");
        return 1;
    }

    FILE *fp = fopen(argv[1], "r");
    if (fp == NULL) {
        printf("ERROR: Could not open the file.\n");
        return 1;
    }

    int ch;
    long lines = 0;

    /* 改行文字の数を数える */
    while ((ch = fgetc(fp)) != EOF) {
        if (ch == '\n') {
            lines++;
        }
    }

    fclose(fp);
    printf("Line breaks: %ld\n", lines);
    return 0;
}

実行方法

コマンドライン引数を指定してプログラムを実行・デバッグするには、プロジェクトのプロパティ設定で引数を指定します。

1.プロパティ画面の左側のメニューから「構成プロパティ」→「デバッグ」を選択します。
2.右側の画面にある「コマンド引数」(またはCommand-line Arguments)の項目に、渡したい引数を入力します。

解説

  • 改行の数を数えると「行数に近い値」が得られます。
  • 最終行に改行が無い場合は、表示上の行数とずれることがある点も理解しておくと完璧です。

演習13-7:数字文字の個数を数える

ファイル中の数字文字 0〜9 の出現回数を数えて表示せよ。

解答例

プロジェクト名:chap13-8-3 ソースファイル名:chap13-8-3.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

int main(int argc, char *argv[])
{
    if (argc != 2) {
        printf("Usage: countdigits <filename>\n");
        return 1;
    }

    FILE *fp = fopen(argv[1], "r");
    if (fp == NULL) {
        printf("ERROR: Could not open the file.\n");
        return 1;
    }

    long digits = 0;
    int ch;

    /* '0' から '9' の範囲なら数字文字 */
    while ((ch = fgetc(fp)) != EOF) {
        if (ch >= '0' && ch <= '9') {
            digits++;
        }
    }

    fclose(fp);
    printf("Digit characters: %ld\n", digits);
    return 0;
}

実行方法

コマンドライン引数を指定してプログラムを実行・デバッグするには、プロジェクトのプロパティ設定で引数を指定します。

1.プロパティ画面の左側のメニューから「構成プロパティ」→「デバッグ」を選択します。
2.右側の画面にある「コマンド引数」(またはCommand-line Arguments)の項目に、渡したい引数を入力します。

解説

  • 文字コード上で '0'〜'9' が連続していることを利用しています。
  • 1文字ずつ読む基本パターンが、そのまま集計処理に応用できるのが分かります。