C言語のきほん|C言語のファイル入出力入門

画面だけで終わらせない。C言語で「保存する力」と「読み出す力」を身につけよう。

これまでのC言語の学習では、printfで画面に結果を表示したり、scanfでキーボードから値を受け取ったりしながら、プログラムの流れを確認してきたと思います。
ただ、実際のプログラムでは、処理したデータをその場で表示するだけでは足りないことがよくあります。たとえば、売上データを保存したい、設定内容を次回起動時にも使いたい、前回の計算結果をあとで読み込みたい、といった場面です。

そんなときに必要になるのが、ファイル入出力です。
ファイル入出力を使うと、プログラムの外にデータを保存したり、保存済みのデータを読み込んだりできるようになります。これによって、プログラムはその場限りの処理から一歩進んで、継続的にデータを扱えるようになります。

C言語では、ファイルを直接むき出しで操作するのではなく、ストリームという仕組みを通して扱います。この考え方を理解すると、画面への出力やキーボード入力と、ファイルへの読み書きが実はよく似た仕組みで動いていることも見えてきます。
また、ファイルにはテキストファイルバイナリファイルがあり、用途によって扱い方が変わります。まずはその入口として、ファイル入出力の基本的な流れと、よく使う関数をしっかり押さえていきましょう。

ファイル入出力とは何か

C言語のファイル入出力とは、プログラムがファイルに対してデータを書き込んだり、ファイルからデータを読み込んだりする仕組みのことです。

たとえば、次のようなことができるようになります。

できること具体例
データを保存する計算結果をファイルに書き出す
保存したデータを再利用する前回保存した設定や記録を読み込む
大量のデータを扱う何百行もの文章や数値を処理する
プログラム終了後もデータを残す実行後も結果をあとで確認できる

画面に表示した内容は、普通はプログラムが終わると消えてしまいます。
でもファイルに保存しておけば、プログラムが終わったあとでもそのデータを残しておけます。これは実用的なプログラムを作るうえで、とても大切な考え方です。

ストリームという考え方を知ろう

C言語では、データの入出力をストリームという仕組みで扱います。
ストリームは、プログラムと外部の入出力先をつなぐ通り道のようなものです。

入出力先には、たとえば次のようなものがあります。

  • キーボード
  • 画面
  • ファイル
  • エラー出力先

つまり、C言語では画面表示もキーボード入力もファイル操作も、同じ「入出力」という大きな考え方の中で扱われています。

たとえば、printfは画面に出力しますが、これは標準出力というストリームに対してデータを送っていると考えられます。
同じように、fprintfを使えば、画面ではなくファイルに向けて出力することもできます。

このように考えると、ファイル入出力は特別な別物ではなく、これまで使ってきた入出力の延長線上にあるものだとわかります。

標準ストリームを確認しよう

C言語では、プログラム開始時にあらかじめ使える標準ストリームが3つ用意されています。

ストリーム役割使用例
stdinキーボードからの入力scanf, getchar
stdout画面への出力printf, putchar
stderrエラーメッセージの出力fprintf(stderr, ...)

それぞれの意味をもう少しやさしく見ると、次のようになります。

  • stdin
    キーボードから入力されたデータを受け取る入口です。
  • stdout
    画面に通常の結果を表示するための出口です。
  • stderr
    エラー情報を出すための出口です。一般には通常の出力とは分けて扱われます。

特に stderr は、エラーメッセージを通常の出力と分離したいときに役立ちます。
たとえば、正常な処理結果は stdout に出し、異常時の案内は stderr に出す、という使い分けができます。

ファイル入出力の基本的な流れ

ファイルを扱うときは、だいたい次の流れになります。

  1. ファイルを開く
  2. ファイルに対して読み書きする
  3. ファイルを閉じる

この流れはとても重要です。
いきなり書き込んだり読み込んだりはできず、まずはファイルを開いて、使い終わったら閉じる必要があります。

流れを図にすると、次のようになります。

この図では、C言語のファイル入出力が開く → 読み書きする → 閉じるという3段階で進むことを表しています。
初心者のうちは、関数の名前をばらばらに覚えようとしがちですが、まずはこの流れを頭に入れることが大切です。

特に重要なのは、最後に必ず fclose で閉じることです。
閉じ忘れると、書き込み内容が正しく保存されなかったり、ファイルの管理に問題が出たりすることがあります。
小さな練習プログラムのうちから、開いたら閉じる、という習慣をつけておくと安心です。

ファイルポインタとは何か

ファイルを操作するためには、ファイルポインタを使います。
これは、ファイルそのものを直接指しているというより、そのファイルを入出力するために必要な情報をまとめて管理するものと考えるとわかりやすいです。

C言語では、ファイルポインタは次のように書きます。

FILE *fp;

この FILE は stdio.h で定義されている型です。
そして fp には、開いたファイルに関する情報が入ります。

ファイルポインタには、主に次のような情報が関係しています。

情報内容
現在位置今どこまで読んだか、どこに書こうとしているか
ファイル終了状態ファイルの終わりまで到達したか
エラー状態読み書き中に問題が起きたか
バッファ情報効率よく入出力するための一時領域の管理

ここで大事なのは、ファイルを扱うには FILE 型へのポインタが必要だということです。
fopen でファイルを開くと、この FILE 型ポインタが返されます。
そのポインタを使って fprintf や fgets などの関数が、どのファイルに対して操作するのかを判断します。

fopen関数でファイルを開く

ファイルを使うには、まず fopen で開きます。

関数宣言は次のとおりです。

#include <stdio.h>

FILE *fopen(const char *restrict filename, const char *restrict mode);

この関数は、filename で指定した名前のファイルを、mode で指定した方法で開きます。
成功すると FILE 型へのポインタを返し、失敗すると NULL を返します。

fopenの基本形

FILE *fp = fopen("data.txt", "w");

この例では、data.txt という名前のファイルを、書き込み用で開いています。

fopenの返却値に注意しよう

fopen は失敗することがあります。
たとえば、存在しないファイルを読み込みモードで開こうとした場合や、権限がない場合です。

そのため、次のように NULL チェックをするのが基本です。

FILE *fp = fopen("data.txt", "r");

if (fp == NULL) {
    printf("ファイルを開けませんでした。\n");
}

この確認をしないまま使うと、エラーの原因になります。
初心者向けの練習でも、fopen の結果確認は必ず入れるようにしておくとよいです。

fopenのモードを整理しよう

fopen の mode にはいくつか種類があります。
ここをあいまいに覚えていると、うっかりファイル内容を消してしまうこともあるので、丁寧に整理しておきましょう。

モード内容ファイルがあるときファイルがないとき
r読み込み専用正常に開くエラーになる
w書き込み専用中身を空にして開く新規作成する
a追加書き込み専用末尾に追加する新規作成する
r+読み書き可能正常に開くエラーになる
w+読み書き可能中身を空にして開く新規作成する
a+読み書き可能末尾に追加する新規作成する

初心者の段階では、まずは次の3つを確実に押さえるのがおすすめです。

モード使いどころ注意点
r既存ファイルを読むときファイルがないと開けない
w新しく書くとき既存内容は消える
a追記したいとき常に末尾に追加される

特に w はとても便利ですが、すでにファイルがある場合、その中身が全部消えてしまいます。
ここは本当に注意が必要です。

たとえば、学習メモを保存するつもりで w を使ったら、前回の内容が全部上書きされてしまった、ということはよくあります。
以前の内容を残したまま追加したいなら、a を使います。

fclose関数でファイルを閉じる

ファイルの使用が終わったら、fclose で閉じます。

関数宣言は次のとおりです。

#include <stdio.h>

int fclose(FILE *stream);

成功すると 0 を返し、失敗すると EOF を返します。

基本的な使い方は次のとおりです。

fclose(fp);

fclose を行う理由は、単に「使い終わったから閉じる」というだけではありません。
ファイルへの書き込みは、効率のためにいったんメモリ上にためてからまとめて保存されることがあります。
そのため、最後に閉じることで、残っているデータがきちんと書き出される意味もあります。

つまり、fclose は後片付けであると同時に、保存を確定させる大切な処理でもあります。

ファイル入出力でよく使う関数

ファイルを開いたあとは、目的に応じていろいろな関数を使います。
代表的なものを整理すると、次のようになります。

種類関数用途
書式付き入出力fprintf, fscanf数値や文字列を書式付きで読み書きする
文字列単位fputs, fgets1行や文字列をまとめて扱う
1文字単位fputc, fgetc1文字ずつ処理する

それぞれ向いている場面が違います。

fprintf と fscanf

printf や scanf に似た感覚で使えるのが fprintf と fscanf です。
違いは、画面やキーボードではなく、ファイルを相手にすることです。

たとえば、fprintf はこう使います。

fprintf(fp, "%d %s\n", 10, "apple");

この場合、fp が指すファイルに、10 apple という内容を書き込みます。

fputs と fgets

文字列や1行単位で扱いたいなら、fputs と fgets が使いやすいです。

fputs("おはようございます。\n", fp);

fgets(str, sizeof(str), fp);

fgets は1行を読み込むときによく使われます。
改行も一緒に読み込むことがあるので、その点は使いながら慣れていくとよいです。

fputc と fgetc

1文字ずつ細かく処理したいときに使います。

fputc('A', fp);

ch = fgetc(fp);

文字ごとの処理が必要な場面では便利ですが、初心者の最初の学習では、文字列単位や書式付き入出力のほうが扱いやすいことが多いです。

サンプルプログラムで書き込みの流れを見てみよう

学習メモをファイルに書き込むプログラムです。

サンプルプログラム

ファイル名:17_1_1.c

#include <stdio.h>

int main(void)
{
    FILE *fp;

    /* 書き込みモードでファイルを開く */
    fp = fopen("memo.txt", "w");

    /* ファイルを開けたか確認する */
    if (fp == NULL) {
        printf("ファイルを開けませんでした。\n");
        return 1;
    }

    /* ファイルに文字列を書き込む */
    fprintf(fp, "C言語の学習メモ\n");
    fprintf(fp, "今日はファイル入出力を練習しました。\n");
    fprintf(fp, "fopen と fclose の使い方を確認しました。\n");

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

    printf("memo.txt に学習メモを書き込みました。\n");

    return 0;
}

このプログラムの動きをやさしく確認しよう

このプログラムは、memo.txt というファイルを開き、3行の文章を書き込んでから閉じる、という流れになっています。

最初に行っているのは、ファイルポインタの宣言です。

FILE *fp;

ここで、あとから開くファイルを管理するための変数を用意しています。

次に fopen でファイルを開きます。

fp = fopen("memo.txt", "w");

ここでは w モードなので、memo.txt が存在しなければ新しく作られ、存在していれば中身を空にして開きます。

そのあとで、きちんと開けたかどうかを確認しています。

if (fp == NULL) {
    printf("ファイルを開けませんでした。\n");
    return 1;
}

この部分はとても大切です。
ファイルを開けない可能性は実際にあるので、確認せずに使うのは危険です。

その次で、fprintf を使ってファイルに文章を書き込んでいます。

fprintf(fp, "C言語の学習メモ\n");

このように、printf と似た感覚で使えますが、出力先が画面ではなくファイルになっている点が違います。

最後に fclose でファイルを閉じます。

fclose(fp);

これでファイル操作は完了です。
そして最後の printf は、画面に対して「書き込みが終わったこと」を知らせています。

実行結果と作成されるファイルの内容

画面には次のように表示されます。

memo.txt に学習メモを書き込みました。

そして、作成された memo.txt の中身は次のようになります。

C言語の学習メモ
今日はファイル入出力を練習しました。
fopen と fclose の使い方を確認しました。

このように、画面表示とは別に、内容がファイルとして保存されるのがファイル入出力の大きな特徴です。

読み込みのイメージもつかんでおこう

今回は主に基本の仕組みが中心ですが、読み込み側も流れはほとんど同じです。
違うのは、開くモードと使う関数くらいです。

たとえば、1行ずつ読み込むなら次のような形になります。

ファイル名:17_1_2.c

#include <stdio.h>

int main(void)
{
    FILE *fp;
    char str[100];

    /* 読み込みモードでファイルを開く */
    fp = fopen("memo.txt", "r");

    /* ファイルを開けたか確認する */
    if (fp == NULL) {
        printf("ファイルを開けませんでした。\n");
        return 1;
    }

    /* 1行読み込んで表示する */
    while (fgets(str, sizeof(str), fp) != NULL) {
        printf("%s", str);
    }

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

    return 0;
}

このプログラムでは、memo.txt を読み込みモードで開き、fgets で1行ずつ読み込んで、printf で画面に表示しています。

ここでも流れは同じです。

  • fopen で開く
  • fgets で読む
  • fclose で閉じる

この一貫した流れが見えてくると、ファイル入出力がだんだん整理しやすくなります。

ファイル入出力で初心者が気をつけたいポイント

ファイル入出力では、最初のうちは似たようなミスがよく起きます。
ここでまとめて確認しておきましょう。

よくあるミス原因対策
ファイルが開けないモード不一致、ファイルが存在しない、権限不足fopen の返却値を確認する
既存データが消えたw モードで開いた追記なら a を使う
書いたはずの内容が反映されないfclose していない処理の最後に必ず fclose する
読み込みがうまくいかない読み込み用の関数の使い方が不十分fgets や fscanf の戻り値を確認する

特に、w モードで上書きされることと、NULL チェックを忘れることは本当によくあるミスです。
最初から丁寧に書く習慣をつけておくと、あとでかなり助かります。

ストリームの視点で見ると理解しやすい

ここまで学んだ内容を、ストリームという視点でまとめ直すと、次のように考えられます。

入出力先使う代表的な関数ストリーム
キーボード入力scanf, getcharstdin
画面出力printf, putcharstdout
エラー出力fprintf(stderr, ...)stderr
ファイルfprintf, fscanf, fgets, fputs などfopen で開いたファイルストリーム

この表を見ると、ファイル入出力だけが特別なのではなく、入出力先がファイルに変わっただけとも言えます。
そのため、printf に慣れている人ほど、fprintf は比較的入りやすいはずです。

なぜファイルポインタが必要なのか

初心者の方の中には、なぜわざわざ FILE 型ポインタを使うのか、不思議に感じる方もいるかもしれません。

これは、プログラムが複数のファイルを同時に開くことがあるからです。
たとえば、A.txt から読み込みながら、B.txt に結果を書き出すような処理もできます。

そのとき、どの関数がどのファイルを対象にするのかを区別するために、ファイルポインタが必要になります。

たとえば次のようなイメージです。

FILE *in_fp;
FILE *out_fp;

このように別々のファイルポインタを持つことで、入力用のファイルと出力用のファイルを区別して扱えます。

つまり、ファイルポインタは「どのファイルに対する操作か」を明確にするための大事な手がかりなのです。

まずはこの考え方を押さえれば十分

ファイル入出力は、最初は少しだけ難しく感じるかもしれません。
でも、覚えるべき土台は意外とシンプルです。

まずは次の考え方をしっかり押さえるだけで十分です。

  • ファイルを使う前に fopen で開く
  • 開けたかどうかを NULL で確認する
  • 目的に応じた関数で読み書きする
  • 最後に fclose で閉じる

この流れが自然に書けるようになると、ファイル保存、ログ出力、設定読込、データ記録など、実用的なプログラムへと一気に近づいていきます。
C言語の学習の締めくくりとしても、ファイル入出力はとても大切なテーマです。
画面に表示するだけだったプログラムが、データを残せるプログラムへ変わる瞬間を、ぜひ手を動かしながら体験してみてください。