Cによるプログラミング入門13
ファイル
今回は、ファイルの扱いについてです。これも、やりだすといろいろなことがあるのですが、ここではサワリの部分だけ、ということにしましょう。
ファイルを扱う手順は次のようなものです。
1. まず、ファイルを開いて、
2. そこに書き出したり、そこから読み込んだりし、
3. 最後にファイルを閉じて終わり
わかりにくいと思いますが、まず、ざっと書いちゃいます。ななめに読んでから、サンプルを見て、それからゆっくり考えてみてください。
ファイルを開くには、fopenという関数が使えます。これは
FILE* fp;
fp = fopen("ファイル名", 開くモード);
のように使います。ここで、FILEは、FILE構造体などとよばれるもので、上のようにすると、fpが開いたファイルを表すようになるのです。これは、ファイルポインタなどともよばれます。
開くモードとしては、主に次のようなものがあります。
"r" | 読み込みモード。ファイルが存在しているとする。 |
"w" | 書き出しモード。すでにファイルがあれば内容を削除し、なければ新たに作成する。 |
"a" | 追加モード。すでにファイルがあればその最後に追加し、なければ新たに作成する。 |
"r+" | 更新モード。ファイルが存在しているとする。 |
"w+" | 更新モード。すでにファイルがあれば内容を削除し、なければ新たに作成する。 |
"a+" | 追加更新モード。すでにファイルがあればその最後に追加し、なければ新たに作成する。 |
ファイルを開くことに失敗した場合、fopenはNULLというものを戻すことになっているので、fopenのあとにfpがNULLでないか調べるのが一般的です。fpがNULLなら、ファイルを開けなかったということになるのです。
開いたファイルに書き出す場合には、たとえば、fprintfという関数が使えます。これは、
fprintf(fp, ...);
のように使うものです。ここで、「...」は、pritnf(あたまにfのついていないprintf)で画面に出力するときと同様のものが使えます。こうすると、fpで表されるファイルに文字列を書き出すことになるのです。これは、下の例を見てから考えてください。
開いたファイルからデータを読み込む場合には、たとえば、fscanfという関数が使えます。
fscanf(fp, ...);
とすると、ファイルからデータを読み込むことになるのです。ここで「...」は、scanf(あたまにfのついていないscanf)のときと同様のものになります。これも、下の例を見てから考えてください。
使ったファイルを閉じるには、次のようにします。
fclose(fp);
これで、fpで表されるファイルが閉じることになっています。
例を見て考えるのがよいと思います。たとえば、ファイルtest.txtというファイルを作成(すでにあればその内容を削除)し、そこに、自分の名前を出力するプログラムは次のように書けます。
/* write_name.c */
#include <stdio.h>
int main()
{
FILE* fp;
fp = fopen("test.txt", "w");
/* 書き出し用にファイルを開く */
if(fp == NULL) return 1; /*
開けなければプログラム終了 */
fprintf(fp, "猫田猫夫\n");
/* ファイルに「猫田猫夫\n」と出力 */
fclose(fp);
/* ファイルを閉じる */
return 0;
}
fopenにより、test.txtが開かれ、プログラム中では、fpがtest.txtというファイルを表すようになります。
しかし、なんらかの不調により、ファイルが開けないこともあるでしょう。その場合、fopenはNULLを戻すことになっているので、fpにはNULLが格納されます。そこで、if文では、fpがNULLかどうかを調べているのです。もし、fpがNULLなら、ファイルを開けなかったということで、これ以上プログラムを進める意味がありません。そこで、returnで終了することにしました。プログラムが成功しなかった場合、0以外の値を戻す慣習があるので、上のサンプルでは1を戻すよう「return
1;」としたのです。もちろん、別の値を戻してもかまいません。いずれにしても、これでmainの実行が終了するのです。
fpがNULLでない場合は、ファイルが開けたので、そのファイルに出力します。それが、「fprintf(fp,
"猫田猫夫\n");」です。これで、fpが表しているtest.txtに「猫田猫夫\n」が書き込まれることになるのです。
Fig.1 write_name.exeを実行しても何も出力されないが...
Fig.2 test.txtができていて、そこには「猫田猫夫」の文字列が書かれている。
ユーザに名前と年齢を聞いて、それをtest.txtに出力するプログラムなら、次のように書くことができます。
/* write_name_age.c */
#include <stdio.h>
int main()
{
FILE* fp;
char name[51];
int age;
printf("名前を入力してください。\n");
scanf("%s", name);
printf("年齢を入力してください。\n");
scanf("%d", &age);
fp = fopen("test.txt", "w");
/* 書き出し用にファイルを開く */
if(fp == NULL) return 1;
fprintf(fp, "%s\n", name);
/* nameをファイルに書き出す */
fprintf(fp, "%d\n", age);
/* ageをファイルに書き出す */
fclose(fp);
return 0;
}
fprintfの扱い方が、第一引数にfpを入れることを除くと、printfと同じであることに注意してください。つまり、「fprintf(fp, ...);」の「...」には、printfのときと同じものを書くのです。簡単なんです。
Fig.3 write_name_age.exeを実行。fprintfは画面への出力ではない。
Fig.4 fprintfのおかげで、test.txtの中身は、次のようになる。
これは、自分で少し実験してみることをお勧めします。
次に、ファイルからデータを読み込む方法を考えてみましょう。たとえば、上のwrite_name_age.exeで作ったファイルtest.txtから、名前と年齢を読み込むプログラムは次のように書けます。
/* read_name_age.c */
#include <stdio.h>
int main()
{
FILE* fp;
char name[51];
int age;
fp = fopen("test.txt", "r");
/* 読み込み用にファイルを開く */
if(fp == NULL) return 1;
fscanf(fp, "%s", name);
/* ファイルから読み込んだ文字列をnameに格納 */
fscanf(fp, "%d", &age);
/* ファイルから読み込んだ整数をageに格納 */
printf("名前: %s\n", name);
printf("年齢: %d\n", age);
fclose(fp);
return 0;
}
Fig.5 read_name_age.exeの実行すると、test.txt内のデータが出力される。
もちろん、同じフォルダにtest.txtを置くことを忘れずに。
このプログラムは、fscanfで、fpからデータを読み込んで、それをあとで出力しているだけです。fscanfは、最初の引数にfpを与えるだけで、あとはscanfと同様に使えることに注意してください。
入門12のkouzoutai3.cを見てください。それは、5人分の社員のデータをユーザに入力させ表示させるプログラムでした。これを改良し、データをmeibo.txtに保存するようにするにはどうしたらよいでしょう。...たとえば、次のようなものが考えられると思います。
/* meibo.c */
#include <stdio.h>
typedef struct
{
int num;
char name[51];
} SMan;
int main(void)
{
int i;
SMan m[5];
FILE* fp;
printf("社員5人分のデータを入力してください。\n");
for(i = 0; i < 5; i++){
printf("社員番号:");
scanf("%d", &m[i].num);
printf("氏名 :");
scanf("%s", m[i].name);
}
printf("全員のデータをmeibo.txtに保存します。\n");
fp = fopen("meibo.txt", "w");
if(fp == NULL) return 1;
for(i = 0; i < 5; i++){
fprintf(fp, "%d\n%s\n",
m[i].num, m[i].name);
}
fclose(fp);
return 0;
}
Fig.6 meibo.exe
Fig.7 meibo.txt
meibo.txtからデータを読み込んで表示するプログラムは課題ということにしましょう。