第36回
ファイルの扱い(1)~オープン/クローズと読み書き

ファイルを読み書きするプログラム

ファイルからデータを読み込むプログラムと、ファイルにデータを書き出すプログラムを作ってみましょう。ファイル操作のための基本的な記述が登場します。

サンプル1~ファイルからデータを読み込む

既存のファイルをオープンしてその内容を読み込み、ディスプレイに表示する処理を作ってみましょう。

オープンするファイルは“C:\CLANG”フォルダに保存した“TEST36a.txt”とします。その内容はリスト1のとおりです。

ファイルからは1文字ずつ読み込み、その都度1文字ずつputchar関数で標準出力へ送り出すことにします。

ファイルから1文字ずつ読み込むには、fgetc関数を使います。

・fgetc関数
宣言:int fgetc(FILE *fp);
機能:fpで示すファイルから1文字(1バイト)読み込み、読み込み位置を1つ進める。
戻り値:読み込んだ1文字。失敗またはファイル終端に達した場合はEOF。

したがって、whileループの継続条件にfgetc関数を使った式を記述し、その結果がEOFでない間は処理を続ける――という構造を作ればよいことになります。プログラムのソースはリスト2のようになります。

プログラムの実行結果は“TEST36a.txt”の内容を出力するだけなので、割愛しておきます。

リスト1:テキストファイル“TEST36a.txt”の内容
When you want to use the file (reading or writing),
Call function 'fopen'.
After using the file -- before terminate the program --
Call function 'fclose'.

----
#include <stdio.h>

int main(void)
{
  FILE *fp;
  int c;

  if ((fp = fopen("C:¥¥CLANG¥¥test36a.txt", "r")) != NULL) {
    while ((c = fgetc(fp)) != EOF) {
      putchar(c);
    }
    fclose(fp);
  } else {
    printf("File open error!¥n");
    return (-1);
  }
  return (0);
}
----
End of file

リスト2:ファイルから1文字ずつ読み込んでは表示するプログラム(ex3601.c
#include <stdio.h>

int main(void)
{
  FILE *fp;    /* ファイルポインタ */
  int c;       /* 1文字を保持 */

  /* ファイルのオープンとエラーチェック */
  if ((fp = fopen("C:¥¥CLANG¥¥test36a.txt", "r")) != NULL) {
    /* ファイル終端まで1文字ずつ読み取る */
    while ((c = fgetc(fp)) != EOF) {
      putchar(c);    /* 読み取った文字を出力 */
    }
    fclose(fp);
  } else {
    /* エラーメッセージを出力 */
    printf("File open error!¥n");
    return (-1);
  }
  return (0);
}

サンプル2~ファイルにデータを書き出す

次に、キーボードから入力した内容を新しく作成したファイルに書き出すプログラムを作ってみましょう。

キーボードからの入力にはgets関数、ファイルへの書き出しにはfprintf関数を使います。

・gets関数 ※4
宣言:char *gets(char *s);
引数:標準入力から読み込んだ文字列をsに格納する。
戻り値:引数sへのポインタ。失敗した場合はNULL。

gets関数は引数の文字列バッファに対するサイズの制約がないため、バッファオーバーラン(バッファサイズ以上の入力を受け取ってしまう状態)の可能性があり、これがコンピュータウィルス侵入の原因となるため、一般に公開するプログラムでは使用を控えるよう推奨されています。今回はあくまでサンプルとして、処理を単純化するために使用しました
・fprintf関数
宣言:int fprintf(FILE *fp, const char *format, ...);
引数:fpで示すファイルに対して、formatで示す形式で続く値のリストを書き出す。
戻り値:ファイルに書き出した文字数。失敗した場合は負の値。

formatで示す書式指定文字列とその後に続く値のリストの書式は、printf関数と同じです。printf関数は、出力先をstdin(標準入力)に限定したfprintf関数です。

プログラムの終了

キーボードからの入力をgets関数で1行(Enterキーが押されるまで)読み取り、その内容をfprintf関数で“TEST36b.txt”に書き出します。whileループの継続条件で文字列が同じかどうかを調べるstrcmp関数を使って入力された文字列を調べ、それが“end”であれば処理を終えることにします。

プログラムのソースはリスト3のようになります。書き出すファイル“TESTT36b.txt”は“C:\CLANG”フォルダにあるものとしています。このファイルがすでに存在していた場合、プログラムを実行した後ではその内容が上書きされます。実行結果は画面1のようになります(あくまで一例です)。

リスト3:キーボードから入力した文字列をテキストファイルに書き出すプログラム(ex3602.c
#include <stdio.h>
#include <string.h>

int main(void)
{
  FILE *fp;
  char buf[256 + 1];

  /* ファイルのオープンとエラーチェック */
  if ((fp = fopen("C:¥¥CLANG¥¥test36b.txt", "w")) != NULL) {
    /* "end"と入力されるまで文字列を入力 */
    while (strcmp(gets(buf), "end")) {
      fprintf(fp, "%s¥n", buf);
    }
    fclose(fp);
  } else {
    /* エラーメッセージを出力 */
    printf("File open error!¥n");
    return (-1);
  }
  return (0);
}



ファイルを扱う準備――オープン/クローズとファイルに対するテキストデータの読み書きを紹介しました。

今回紹介した関数とその扱いは非常に基本的なもので、パターンとして覚えておくといいでしょう。プログラムが大規模になり構造が複雑になっても、この基本的な手続きは変わりません。

あとがき

hiropの『ちょっと気になる専門用語』~《読み込む/読み出す??》

本文では、ディスクからデータを読む動作を「読み込む」、書く動作を「書き出す」と表現しました。ところが、「読み出す」「書き込む」という表現もよく使われています。読むときに「込む」のか「出す」のか?はたまた書くときに「込む」のか「出す」のか?……どちらが正しいのでしょう?

実は、どちらも間違っていません。これは、データを読み書きする主体をコンピュータと人間のどちらに置くかで変わってくる表現なのです。

コンピュータを主体にすれば、その外部にあるデータを受け入れる行為は「読み込む」であり、処理結果を提示する行為は「書き出す」となります。「入力/出力」という言葉が、コンピュータを中心にしていることと関連しています。

しかし、コンピュータを扱う人間の側に立てば、コンピュータに対してデータを「読み出させる」のであり、処理結果はディスクやプリンタに「書き込ませる」というイメージになります。

技術的な文章の場合、基本的にはコンピュータを処理の主体とするのが一般的なので、前者の「読み込む/書き出す」を使う方が自然です。ユーザー向けの説明では、後者の「読み出す/書き込む」を使った方がよいかもしれません。

重要なことは『読み出したら書き込む/読み込んだら書き出す』という形で動詞の整合性を図ることです。読み出して書き出したり、読み込んで書き込んだりすると、読む側が混乱してしまいます。