教科書P226〜227
今まで、データの入力は標準入力装置(キーボード)から行い、出力は標準出力装置(ディスプレイ)へしていた。
では、データの入出力をファイル(テキスト・ファイル)で行うにはどうすればよいだろうか?例えばscanfは書式付き標準入力関数、printfは書式付き標準出力関数であるため、基本的にはファイルからデータを読み込んだり、ファイルに出力することはできない(実行時にリダイレクションを使えば可能)。
C言語には、ファイルを操作する様々な関数が用意されているため、それらを利用してファイルへの入出力を行う。
教科書P228〜229
ファイルを利用するには、プログラム上でオープンとクローズを行わなければならない。サンプルは次のとおり。
#include <stdio.h> #include <stdlib.h> void main(void) { char filename[] = "data.txt"; /* データファイル名 */FILE *fp;/* ファイルを開く */ if ((fp = fopen(filename, "r")) == NULL) { printf("ファイルが存在しません。プログラムを終了します。\n"); exit(1); } /* データ処理 */ ・ ・ ・ /* ファイルを閉じる */fclose(fp);}
- FILE *fp;
- C言語でファイルを扱う場合、プログラム上で使用するファイルにはファイル・ポインタを割り当てる必要がある。複数のファイルを同時に扱う場合、同じ数のファイル・ポインタを宣言する。
FILE構造体はstdio.hで定義されているため、プログラムには必ずstdio.hをインクルードしなければならない。- fp = fopen(filename, "r")
- FILE構造体で宣言したファイル・ポインタ「fp」に対して、ファイルをオープンする。fopen関数は2つの引数を持ち、1つ目の引数はファイル名(文字列)を、2つ目の引数にはオープン・モードを指定する。
オープンモードには次の種類がある。fopen関数は、ファイルのオープンに成功すると、ファイル・ポインタをオープンしたファイルの先頭(アペンド・モードの場合はファイル・エンド)にセットし、失敗するとNULLを返す。サンプルでは、NULLが返ってきたらメッセージを出してプログラムを終了するようにしてある。(exit関数を使うにはstdlib.hをインクルードしなければならない)
モード 意味 ”r” リード(読み込み)・モード
ファイルはすでに存在していなければならない。”w” ライト(書き込み)・モード
ファイルがなければ作成され、あれば中身が空になる。”a” アペンド(追加書き込み)・モード
ファイルがなければ作成され、あればファイルの現在位置をファイル・エンドにセットする。- fclose(fp);
- オープンしたファイルをクローズする。プログラム終了時、またはファイルが必要なくなったときに必ずクローズしなければならない(実際はリード・モードでオープンしたファイルはクローズしなくても実害はないが、開いたファイルはすべてクローズするようにプログラムするべきである。)。
ファイル名を指定する際、パスを指定しなかった場合はプログラムを実行した場所(exeファイルがある場所)からファイルを探す。
Microsoft Visual C++の場合、プロジェクトから実行すると、プロジェクト・フォルダの中を探すが、ビルドによって作成されたexeファイルをダブルクリックして実行すると、exeファイルがあるフォルダ内を探す。exeファイルを配布する場合はデータ・ファイルの配置に注意する必要がある。ファイル名にパスを指定する場合、例えばC:\data\data.txtを読み込む場合、プログラム上で次のように指定しなければならない。(Windows系の場合のみ)
char filename = "c:\\data\\data.txt" ※「¥」はエスケープ文字を表すための記号なので、「¥」という文字を使いたい場合は「¥¥」と指定しなければならないことに注意!!
教科書P230〜231
ファイルのオープンに成功すると、ファイル・ポインタを用いてファイルからデータを入力したり、ファイルへ出力したりできるようになる。ここでは、1文字単位でデータを入出力する関数を紹介する。
《機能》
getchar関数のファイル・ポインタ版。引数に指定したファイル・ポインタから1文字(8ビット)読み込み、ファイル・ポインタを1文字(8ビット)移動させる。
ファイル・ポインタがファイルの一番最後(ファイル・エンド)までくると、EOF(−1)を返す。《使用例:ファイルから1文字ずつ読み込み、CRTに縦に出力する》
#include <stdio.h> #include <stdlib.h> void main(void) { char filename[] = "data.txt"; FILE *fp;int c;/* ファイル・オープン */ if ((fp = fopen(filename, "r")) == NULL) { printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename); exit(1); } /* データ処理 */ while ((c = getc(fp)) != EOF) { printf("%c\n", c); } /* ファイル・クローズ */ fclose(fp); }《プログラム解説》
- int c;
- getcで読み込んだデータ(8ビット)を格納する変数の宣言。char型ではないことに注意。
- fp = fopen(filename, "r")
- ファイルからデータを読み込むため、リード・モードで開く。
- (c = getc(fp)) != EOF
- ファイル・ポインタから1文字(8ビット)分読み込み、int型変数cに格納する。戻り値がEOFでない間、while文を繰り返す。
※getc関数は改行文字も1文字として入力してくる。
《機能》
putchar関数のファイル・ポインタ版。引数に指定したファイル・ポインタへ1文字(8ビット)出力し、ファイル・ポインタを1文字(8ビット)移動させる。
《使用例:文字列を1文字ずつファイルに出力する》
#include <stdio.h> #include <stdlib.h> void main(void) { char filename[] = "out.txt"; FILE *fp; int i; char data[] = "putc test-pro"; /* ファイル・オープン */ if ((fp = fopen(filename, "w")) == NULL) { printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename); exit(1); } /* データ処理 */ for (i=0 ; data[i]!='\0' ; i++) {putc(data[i], fp);} /* ファイル・クローズ */ fclose(fp); }《プログラム解説》
- fp = fopen(filename, "w")
- ファイルへ出力するため、ライト・モードで開く。
- putc(data[i], fp);
- ファイル・ポインタの位置に1文字(8ビット)出力し、ファイル・ポインタを1文字(8ビット)ずらす。
ファイルから1文字ずつ読み込んで、別のファイルに1文字ずつ出力するプログラムを作成しなさい。(出力したファイルは、読み込んだファイルとまったく同じ内容になる)
なお、読み込むデータファイルは自分で作成すること。
教科書P234〜237
getcやputc関数のように1文字ずつではなく、1行ずつ入出力を行う関数を紹介する。
《機能》
gets関数のファイル・ポインタ版。ファイル・ポインタから1行(\nまで)読み込んで文字配列に格納し、ファイル・ポインタを次の行の先頭へ移動させる。
ファイル・ポインタがファイルの一番最後(ファイル・エンド)までくる、または読み込みエラーが発生すると、NULL(0)を返す。《使用例:ファイルから1行ずつ読み込み、CRTに出力する》
#include <stdio.h> #include <stdlib.h>#define MAX_STRINGS 80void main(void) { char filename[] = "data.txt"; FILE *fp;char data[MAX_STRINGS];/* ファイル・オープン */ if ((fp = fopen(filename, "r")) == NULL) { printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename); exit(1); } /* データ処理 */ while (fgets(data, MAX_STRINGS, fp) != NULL) { printf("%s", data); } /* ファイル・クローズ */ fclose(fp); }《プログラム解説》
- #define MAX_STRINGS 80
- fgets関数は、2つ目の引数で「読み込む最大文字数」を指定しなければならないため、80文字にした。この場合、読み込むデータの長さは改行文字を含めて80文字以内にしなければならない。
- char data[MAX_STRINGS];
- getsで読み込んだデータ(改行文字までの任意の長さを持つ文字列)を格納する変数の宣言。
- fgets(data, MAX_STRINGS, fp) != NULL
- ファイル・ポインタから1行(改行文字まで)読み込み、文字配列dataに格納する。戻り値がNULLでない間、while文を繰り返す。
※MS−DOS系OSの場合、1行の終わりはCR(復帰)・LF(改行)の2文字で行っているが、gets関数はCR・LFの2文字を¥nの1文字に変換し、文字列の終わりに¥0を付与してから、文字配列に格納する。
《機能》
puts関数のファイル・ポインタ版。引数に指定したファイル・ポインタへ1行出力し、ファイル・ポインタを次の行へ移動させる。
《使用例:文字列をファイルに出力する》
#include <stdio.h> #include <stdlib.h> void main(void) { char filename[] = "out.txt"; FILE *fp; char data[] = "puts test-pro"; /* ファイル・オープン */ if ((fp = fopen(filename, "w")) == NULL) { printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename); exit(1); } /* データ処理 */fputs(data, fp);/* ファイル・クローズ */ fclose(fp); }《プログラム解説》
- fp = fopen(filename, "w")
- ファイルへ出力するため、ライト・モードで開く。
- puts(data, fp);
- ファイル・ポインタの位置に文字配列を出力し、ファイル・ポインタを次の行へ移動させる。¥0はCR・LFに変換してから出力する。
ファイルから1行ずつ読み込んで、別のファイルに1行ずつ出力するプログラムを作成しなさい。(出力したファイルは、読み込んだファイルとまったく同じ内容になる)
教科書P238〜239
標準入出力関数「scanf」「printf」のファイル・ポインタ版である「fscanf」「fprintf」関数を紹介する。これらを利用することにより、自分の好きなフォーマット(書式)でデータの入出力ができる。
《機能》
scanf関数のファイル・ポインタ版。ファイル・ポインタから指定した書式に従って1行(\nまで)読み込み、書式に対応する各変数にデータを格納する。1行読み込んだら、ファイル・ポインタを次の行の先頭へ移動させる。
ファイル・ポインタがファイルの一番最後(ファイル・エンド)までくる、または読み込みエラーが発生すると、EOF(−1)を返す。《使用例:ファイルから1行ずつ読み込み、CRTに出力する》
#include <stdio.h> #include <stdlib.h> void main(void) { char filename[] = "data.txt"; FILE *fp;int number;char name[20];/* ファイル・オープン */ if ((fp = fopen(filename, "r")) == NULL) { printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename); exit(1); } /* データ処理 */ while (fscanf(fp, "%d,%s", &number, name) != EOF) { printf("番号:%d 氏名:%s\n", number, name); } /* ファイル・クローズ */ fclose(fp); }《入力データ例》
1,Arai
2,Ando
3,Ohishi
4,Komine
5,Sakata※1行のデータ内にカンマ区切りで番号と名前が登録されている。
《プログラム解説》
- int number;
char name[20];- fscanf関数で読み込んだデータを格納する変数の宣言。
- fscanf(fp, "%d,%s", &qmp;number, name) != EOF
- ファイル・ポインタから
指定した書式で1行読み込み、変数に格納する(今回はカンマ区切りで1つ目は整数、2つ目は文字列)。戻り値がEOFでない間、while文を繰り返す。
《機能》
printf関数のファイル・ポインタ版。引数に指定したファイル・ポインタへ指定した書式で1行出力し、ファイル・ポインタを次の行へ移動させる。
《使用例:書式指定してファイルに出力する》
#include <stdio.h> #include <stdlib.h> void main(void) { char filename[] = "out.txt"; FILE *fp; int number = 15; char data[] = "puts test-pro"; /* ファイル・オープン */ if ((fp = fopen(filename, "w")) == NULL) { printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename); exit(1); } /* データ処理 */fprintf(fp, "番号:%d データ:%s\n", number, data);/* ファイル・クローズ */ fclose(fp); }《プログラム解説》
- fprintf(fp, "番号:%d データ:%s\n", number, data);
- ファイル・ポインタの位置に指定した書式でデータを出力し、ファイル・ポインタを次の行へ移動させる。
ファイルから1行ずつ読み込んで、別のファイルに1行ずつ出力するプログラムを作成しなさい。
なお、入力データは1行に2つ以上の任意のデータを任意の書式で作成し、出力データは「項目名1:値 項目名2:値」という書式で出力すること。
教科書P229補足
ファイル・オープン時、モードに”a”を指定すると、ファイルへの追加書き込みになる。例えば次のプログラムを作成し、実行すると、実行するたびに出力・ファイルにデータが増えていく。
#include <stdio.h> #include <stdlib.h> void main(void) { FILE *fp; /* ファイルをアペンド・モードで開く */ if ((fp = fopen("out.txt", "a")) == NULL) { printf("ファイルのオープンに失敗しました。プログラムを終了します。\n"); exit(1); } /* ファイルへ1行出力 */ fputs("プログラムを実行するたびに行数が増えるぞぉ\n", fp); /* ファイルのクローズ */ fclose(fp); }使い道がありそうでないような気が・・・