たいていのプログラムでは、起動したらなんらかのデータを読み込む。 そしてデータを処理した結果を書き出す。 プログラムによっては、毎回必ず同じデータを読むことがある。たとえば 商品と値段表や、学籍番号と名前、などのように一度決まったら しばらく同じものを使うような性質のデータを処理する場合である。 そのような場合は、決まったデータをファイルに保存しておいて、 プログラムが勝手にそのファイルを開いてデータを読み込むようにしておくと 都合が良い。
データの読み込みや書き出しを特定のファイルに対して行なうための
関数が fopen/fclose
である。
ファイルから、データを読むためには以下の手順を取る。
fopen
関数でファイルを開く
fclose
関数でファイルを閉じる
キーボードからデータを読ませるときは、1と2と4の手順は要らず fgetsだけで良かった。以下は、標準入力から 読んだデータを行番号をつけて表示するプログラムである。
#include <stdio.h> #define BUF_LEN 200 int main() { char buffer[BUF_LEN]; int n=0; while (NULL != fgets(buffer, sizeof buffer, stdin)) { printf("%4d: %s", ++n, buffer); } }
挙動を確かめるため、試しにコンパイルと実行をしてみよう。
% gcc -o cat-n cat-n.c % ./cat-n てきとうに 何行か入れて、 最後の行でこんとろーるDをおす [C-d] % cat aisatsu.c | ./cat-n
では、データを必ず aisatsu.c
(第4回に作った) から読み込むように
fopen, fclose
を利用して改良してみよう。
#include <stdio.h> #define BUF_LEN 200 int main() { FILE *aisatsuin; char buffer[BUF_LEN]; int n=0; aisatsuin = fopen("aisatsu.c", "r"); while (NULL != fgets(buffer, sizeof buffer, aisatsuin)) { printf("%4d: %s", ++n, buffer); } fclose(aisatsuin); }
コンパイルし、プログラムを起動すると自動的にデータファイルとして
aisatsu.c
を開き、行番号をつけた結果を出力する。
ファイルから入力させるために施した修正点について説明しよう。
FILE *aisatsuin;
FILE
構造体へのポインタを持つ変数を宣言している。
FILE
構造体とは、開くファイルに関する様々な情報を
まとめて記憶しておくための型である。「構造体」に関しては後期に
学習する。変数名の直前についている *
は、その変数は
値を持つのではなく、値が入っている「アドレス」を持つのだということ
を意味している。アドレスを持つための変数のことをポインタ変数
という。
ファイルを開くために宣言する変数については、あまり中味にこだわっ てもしかたないので、
ファイルを開くときは、そのファイルの情報を持つ変数を
FILE *
で宣言すれば良い
とだけ覚えておけば良い。
aisatsuin = fopen("aisatsu.c", "r")
実際にファイルを開き、その情報をaisatsuinに代入する。
fopen
関数の利用書式は
以下のとおり。
fopen
- ファイルのオープン
ファイルを指定したモードで開く
#include <stdio.h> fopen(ファイルのパス, モード)
第1引数に指定したファイルを開く。そのときにモード を表す文字列を指定できる。モードには以下のものがある。
"r"
ファイルを読み取るためにテキストモードでオープンする。 ファイルの先頭から読む。
"r+"
ファイルを読み取りと書き込みのめにオープンする。 ファイルの先頭から読む。
"w"
書き込み(新規作成)モードで開く。既存のファイルは0バ イトにクリアされる。
"w+"
読み書きモードで開く。既存のファイルは0バイトにクリ アされる。
"a"
書き込みモードで開く。ファイルが既に存在する場合は ファイルの終わりから開始し、なければ新規に作成する。
"a+"
読み書きモードで開く。ファイルが既に存在する場合は ファイルの終わりから開始し、なければ新規に作成する。
ファイルのオープンに成功すると、開いたファイルの情報を持
つFILE
構造体へのポインタを返す。失敗した場合は
NULL
を返す。
fgets(buffer, sizeof buffer, aisatsuin)
fgets
の第3引数にあたえるストリームとして
開いたファイルへのFILEポインタを指定することで、そのファイルから
入力するようになる。
fclose(aisatsuin);
FILEポインタを与えてファイルをクローズする。 忘れやすいので注意!
fprintf
によるファイルへの書き出しfprintf
関数を使うと、
fopen
で書込モードでオープンしたファイルに書き出せる。
FILE *kakikaki;
:
kakikaki = fopen("hello.dat", "w");
:
fprintf(kakikaki, "Hello, world!\n");
:
fclose(kakikaki);
fprintf
関数は、printf
関数と同様に
書式つきの出力を行なう。第1引数に出力先ストリームを指定する点を
除いてprintf
関数と同じだと思って良い。
printf("%s は %d 円です.\n", item, price); は以下と同じ fprintf(stdout, "%s は %d 円です.\n", item, price);
fopen
を利用したプログラムでは、
開こうとしたファイルが存在しない!!
ということがごく普通にありうるので、プログラムを書くときは、
「開くファイルがなかった場合」つまり、fopen()
に
失敗した場合を必ず考えなければならない。具体的には、fopen()
関数の返却値が NULL
の場合の対処を書く必要がある。
開こうとしたファイルがなかったら、続く処理が何もできないことが 多いだろうから、以下のような流れを書けば良い。
FILE *datafile;
char buf[100];
datafile = fopen("hissu.data", "r");
if (NULL == datafile) {
fprintf(stderr, "hissu.data ファイルを用意して下さい\n");
exit(1); /* exitはプログラムの実行を終了 */
}
while (NULL != fgets(buf, 100, datafile)) {
:
: (本当の処理)
}
fprintf
の出力先として指定している
stderr
は、標準エラー出力 と呼ばれるもので、
エラーメッセージを出すためのストリームである。エラーメッセージを
if (NULL == datafile) { /* ↓ エラーメッセージにprintfはダメ! */ printf("hissu.data ファイルを用意して下さい\n"); exit(1); /* exitはプログラムの実行を終了 */ }
のように標準出力に出した場合
% cat aisatsu.c | ./cat-n > hogehoge.out
のように、リダイレクトされた場合エラーメッセージまで
hogehoge.out
ファイルに行ってしまって、エラーになったかどう
かが利用者には分からない。標準エラー出力
(stderr
)に出すようにすれば、たとえリダイレクトされても
必ず画面にメッセージが出るようになる。