テキストファイルの読み書き
ファイルの取り扱い
今までは、入出力を行う場合、すべて画面に表示してきました。
画面に表示すれば、結果がすぐにわかって便利だからです。
しかし、画面に表示された結果は、プログラムが終わると消えてしまいます。
また、膨大な量のデータを画面に表示するのは現実的ではありません。
この様な場合、ファイルにデータを保存するのが普通です。
ファイルとして保存されたデータは、ディスク上に保存されるので、
消えることはなく、コピーや再編集が容易です。
ここでは、C言語でファイルを読み書きする方法を説明します。
ファイルの開閉
プログラムからファイルを操作する手順は、おおむね次のような順番で行われます。
ファイルを開く -> ファイルに読み書きする -> ファイルを閉じる
つまり、ファイル操作には、
ファイルの開閉が必須となっています。
そこで、C言語には、ファイルを開閉する関数が用意されています。
開く関数が
fopen関数、閉じる関数が
fclose関数です。
この関数を使うには、stdio.h のインクルードが必要になります。
それぞれの関数の使い方は、次の通りになります。
FILE型のポインタ変数 = fopen(ファイル名, モード);
fclose(FILE型のポインタ変数);
ファイル名とは、その名の通りのファイル名です。
フルパスで指定することも、名前だけで指定することもできます。
モードとは、ファイルを開く目的を表す文字列のことです。
モードには、次の6種類の文字列の内どれか1つを指定します。
モード文字列 |
目的 |
r |
読み込み。ファイルがない時は失敗。 |
r+ |
読み書き。ファイルがない時は失敗。 |
w |
書き込み。ファイルがあっても空のファイルを作る。 |
w+ |
読み書き。ファイルがあっても空のファイルを作る。 |
a |
追加書き込み。ファイルがない時は作る。 |
a+ |
追加読み書き。ファイルがない時は作る。 |
FILE型は今までに出てきたことがない新しい型ですが、その正体は構造体です。
fopen関数を実行すると、ファイル情報を持つFILE型へのポインタが返されます。
このポインタは、以後、開いたファイルの識別子として使うだけなので、
ポインタ特有の操作を行ったり、構造体の要素を使うことはありません。
慣習的に、FILE型へのポインタ変数を、
ファイルポインタと呼びます。
ここまで説明したことがわかれば、ファイルの開閉ができます。
次のプログラムは、test.txtと言う名前のファイルを書き込みのために開く例です。
#include <stdio.h>
int main(void)
{
FILE *file;
file = fopen("test.txt", "w");
fclose(file);
return 0;
}
このプログラムを実行すると、test.txtという名前のファイルが作成されます。
今回は開いただけなので、当然中身は空っぽです。
今回のプログラムだけだと、fclose関数はとくに必要ないように見えてしまいます。
しかし、fclose関数には重要な役割がありますので、忘れてはいけません。
現代のパソコンでは、同時に複数のアプリが動作できます。
もし、同じファイルを同時に別々のアプリで書き換えてしまうと、
どちらを反映すれば良いのかわからなくなってしまいます。
そこで、fopen関数で書き込みができるように開いたファイルには、
他のソフトで書き換えられないよう、ロックしています。
fclose関数はロックを解除して、他のアプリから使えるようにするために必要です。
ファイルへの書き込み
ファイルへテキストを書き込む関数は、多くの種類が用意されています。
その中に、馴染みのprintf関数と良く似た、fprintf関数があります。
fprintf関数の使い方は、次の通りです。
fprintf(ファイルポインタ, 書き込み文字列, 変数・・・);
使い方は、ファイルポインタを指定すること以外、printf関数とまったく同じですが、
指定した文字列は、画面ではなく
ファイルに書き込まれます。
次のプログラムは、test.txtファイルに Hello,world の文字列を書き込みます。
#include <stdio.h>
int main(void)
{
FILE *file;
file = fopen("test.txt", "w");
fprintf(file, "Hello,world");
fclose(file);
return 0;
}
このプログラムを実行すると、test.txtファイルの中身は次のようになります。
test.txtファイルは、実行ファイルと同じフォルダに作られます。
printf関数と同じように、変数の値を書き込むこともできます。
次のプログラムは、test.txtファイルに変数iの値を書き込みます。
#include <stdio.h>
int main(void)
{
int i = 100;
FILE *file;
file = fopen("test.txt", "w");
fprintf(file, "%d", i);
fclose(file);
return 0;
}
このプログラムを実行すると、test.txtファイルの中身は次のようになります。
読み込みモードで開いた場合、書き込み用の関数を使っても何も起きません。
追加モードで開いた場合、元のファイルの最後にデータが追加されます。
ファイルからの読み込み
ファイルのテキストを読み込む関数にも多くの種類がありますが、
馴染みのscanf関数に似た
fscanf関数があります。
先頭でファイルポインタを指定することを除き使い方はscanf関数と同じです。
scanf関数はキーボートから読み込むため、実行すると入力待ちになりましたが、
fscanf関数では、ファイルのテキストを先頭から読み込みます。
次のプログラムは、test.txtファイルから先頭の数字を読み込んで表示します。
#include <stdio.h>
int main(void)
{
int i;
FILE *file;
file = fopen("test.txt", "r");
fscanf(file, "%d", &i);
fclose(file);
printf("%d\n", i);
return 0;
}
このプログラムの実行結果は、test.txtファイルの中身によって異なります。
test.txtファイルの中身が次の通りだった場合、実行結果は次の通りになります。
上の例のように%d指定子を使用した場合、数字以外のテキストは無視されます。
test.txtファイルの中身が次の通りだった場合、実行結果は次の通りになります。
文字列の入力に使用する%s指定子を使用すれば文字列全体を読み込めます。
しかし、スペースなどが含まれる場合にはそこまでしか読み込まれません。
また、複数の数値をカンマ(,)で区切って並べてやれば、
複数の変数を読み込むことも可能です。
#include <stdio.h>
int main(void)
{
int i, j;
FILE *file;
file = fopen("test.txt", "r");
fscanf(file, "%d,%d", &i, &j);
fclose(file);
printf("i = %d : j = %d\n", i, j);
return 0;
}
この様にカンマ(,)で区切って数値や文字列を並べたファイルを
CSV形式と呼びます。
エクセルなどの表計算ソフトで扱える汎用的なファイル形式として知られています。
上記のプログラムによって、エクセルで保存したCSVファイルを、自作プログラムで読み込むことも可能です。
エクセルで保存したCSVは複雑になりがちなのでそれほど簡単ではありませんが・・・