ファイル操作

ファイル操作


サイト全体の目次

稿


稿

書き込み

このセクションのソース

配列、構造体と、扱うデータ量が増えてくると必然的に、それらのデータを別のファイルに保存したい、あるいは別のファイルに保存してあるデータを参照した、いという要求がでてくるでしょう。そこで今回はファイル操作をする方法の説明です。また、今回使うファイル操作に関わる関数及び変数型「FILE」の定義は「stdio.h」にあります。

/*01*/ #include <stdio.h>
/*02*/ #define FNAME "d:\\my documents\\data.txt"
/*03*/ 
/*04*/ main()
/*05*/ {
/*06*/   int age;
/*07*/   char name[32];
/*08*/   FILE *fp;
/*09*/ 
/*10*/   printf("あなたの名前を入力してください。 -> ");
/*11*/   scanf("%s", name);
/*12*/   printf("あなたの年齢を入力してください。 -> ");
/*13*/   scanf("%d", &age);
/*14*/ 
/*15*/   fp = fopen(FNAME, "w");
/*16*/   fprintf(fp, "Hello, world.\n");
/*17*/   fprintf(fp, "あなたの名前は %s です。\n", name);
/*18*/   fprintf(fp, "あなたの年齢は %d です。\n", age);
/*19*/   fclose(fp);
/*20*/ 
/*21*/   printf("処理が終わりました。\n");
/*22*/ 
/*23*/   return 0;
/*24*/ }

list-7.2.1-02

ファイルを開く時は fopen 関数を使い、引数部分に対象となるファイルの名前を書くのですが、ズラズラとファイル名を書くのはあまりいい方法ではないので、ファイル名を定義しておきましょう。(ファイル名を定義しておくか、直接書くかは状況によって使い分けてください。)

この時、2つ注意して欲しい点があります。

1つ目は、フォルダ(ディレクトリ)の指定方法です。ディレクトリを指定する時、Win 上では「\(もしくはバックスラッシュ)」記号を使いますが、「\」記号というのはエスケープシーケンスをあらわす特別な記号です。ですから、「\\」というふうに記号を2つ続けて書かなければいけません。(UNIX では「/」記号のパス形式に従う:「//」とする必要はありません。また、Windows も「\」の代わりに「/」を使うことができます。)

2つ目は、ファイルの場所です。この例では、ファイルの場所をフルパス(つまり明確に)で書いています。こうすると、確かにその場所のファイルを参照するようになります。これに対し、ファイル名だけ書くと、プログラムのあるディレクトリを参照します。そのプログラム用のデータファイルであれば、こちらの方法を使ってプログラムとセットで扱うようにしてもいいでしょう。また、プログラムが動いている(つまりプログラムがある)ディレクトリを「カレントディレクトリ」といいます。ちなみに Visual C++ の場合、コンパイルされたファイルは、ソースファイルのあるフォルダに Debug というフォルダが作られ、その中に実行ファイルが作られますよね。この時、普通に(つまりエクスプローラなどから)実行した場合、カレントディレクトリはプログラムのあるディレクトリになりますが、Visual C++ から試験実行した場合、カレントディレクトリはソースファイルのあるディレクトリになります。実用化する時は気をつけてください。

また、ファイル名は拡張子も含め好きなものにしてかまいません。Win 環境で使っているのでしたらメモ帳などで編集できるように拡張子を「txt」としてもいいでしょう。(拡張子ごとにデータの保持形式が変わるということはありません。ですから、拡張子を dat などとしてもメモ帳で開くことができます。)

list-7.2.1-08

FILEは typedef 宣言された構造体です。実際のファイル操作は、これのポインタ(ファイルポインタと呼ばれる:この例では「fp」)を使って行います。

list-7.2.1-15

fopen関数を使って fp で操作するファイルを開きます。書式は

ファイル・ポインタ = fopen(ファイル名, モード);
例:
fp = fopen(FNAME, "w");

です。いずれの引数も文字列として渡します。ここで「モード」とは、そのファイルをどのような目的で開くのかを指定します。この指定したファイルモードで、ファイルポインタ(ここでは fp)を使い、ファイルが操作できるようになります。最もよく使うモードは「"w"」「"a"」「"r"」の3種類で、それぞれ「書き込み(write)」「追加書き込み(append)」「読み込み(reed)」を意味します。

この時「"w"」と「"a"」は、指定されたファイルが見つからなかった場合、指定された名前の空のファイルが自動生成されます。指定されたファイルがある場合は、「"w"」ならファイルの内容を上書き保存し、「"a"」ならそのファイルの1番後ろから追加書き込みします。(「"w"」の場合、当然それまでのデータは消去されます。)

それに対し「"r"」はファイルが見つからなかった場合、fopen 関数が「NULL」を返してきますので、これを利用してエラー制御を行ないましょう。下は「"r"」モードによる、ファイルのオープンとエラー発生時の処理をまとめたものです。

if((fp = fopen(FNAME, "r")) == NULL){
    printf("ファイルがありません。終了します。\n");
    return 0;
}

ただし、「"w"」や「"a"」でもディレクトリが無いなどの理由で NULL を返してくる場合があるので、list-7.2.1 にもエラー処理を追加するとよいでしょう。

また、モードごとの違いをまとめておきました。

モード   ファイルが無かった時    ファイルに対する操作
"w"      自動生成               ファイルにデータを上書き
"a"      自動生成               ファイルにデータを追加書き込み
"r"      NULL を返す            ファイルのデータを読みこむ
"w+"     自動生成               ファイルにデータを上書き、読み込みも可
"a+"     自動生成               ファイルにデータを追加書き込み、読み込みも可
"r+"     NULL を返す            ファイルのデータを読みこむ、書き込みも可

list-7.2.1-16

さて、実際に開かれたファイルはどのように操作されるのでしょうか。それほど難しくありません。画面への出力、キーボードからの入力とほとんど同じで、ファイルを1枚の紙に見たてれば左上から順にファイ・ポインタが移動して、書き込みや読み込みを行ないます。そういう意味では、ファイルは「紙」、ファイ・ポインタは「ペン」と見たてることができるかもしれません。

では、実際の書き込みです。fprintfという関数がありますね。名前からもわかるとおり、printf 関数と同等の機能をファイルに対して行ないます。書式は、第1引数にファイルポインタを書くだけで、後は機能も使い方も printf と同じですから、ファイルに対してどのような書きこみが行なわれるか想像できると思います。ですがこの時、ファイルポインタはどのように動くのでしょうか。

name に totobon, 年齢に 300 と入力した場合

fprintf(fp, "Hello, world.\n");    /*(1)*/
fprintf(fp, "あなたの名前は %s です。\n", name);    /*(2)*/
fprintf(fp, "あなたの年齢は %d です。\n", old);    /*(3)*/

/* 以下、ファイルに書きこまれた内容と、各 fprintf 後の fp の位置 */
(1):Hello, world.<(fp)
(2):あなたの名前は totobon です。<(fp)
(3):あなたの年齢は 300 です。<(fp)
    /*「<」は改行コード(つまり「\n」)*/

各書きこみが終わると、ファイル・ポインタはその場所で止まっています。(もちろん、番号と fp という文字は表示されません。)このファイル・ポインタの移動は、fclose でファイルを閉じるまで続きます。

また、書きこみを行なう関数は fprintf のほかにもいくつか用意されています。

fputc(1文字, fp);    /*1文字を書き込む*/
fputs(文字列, fp);    /*文字列を書きこむ*/
    /*「fp」はファイルポインタ*/

list-7.2.1-19

最後にファイルに対する操作が終わったら、ファイルを閉じます。下のほうに「fclose」という関数がありますね。これの引数に操作したファイルのファイル・ポインタを書くだけです。

fclose(ファイル・ポインタ);
例:
fclose(fp);

ちなみに、fclose では引数を1つしか取れませんので、複数のファイルを開いた場合、その分だけ fclose で閉じなければいけません。また、この作業を忘れてもコンパイラはエラーであると教えてくれませんので、気をつけてください。

読み込み

このセクションのソース

次は、ファイル内容を読みこんでみましょう。対象ファイルは、上のリストで作ったファイルであるとします。

/*01*/ #include <stdio.h>
/*02*/ #define FNAME "d:\\my documents\\data.txt"
/*03*/ 
/*04*/ main()
/*05*/ {
/*06*/   char ch;
/*07*/   FILE *fp;
/*08*/ 
/*09*/   if((fp = fopen(FNAME, "r")) == NULL){
/*10*/     printf("ファイルがありません。終了します。\n");
/*11*/     return 0;
/*12*/   }
/*13*/   while((ch = fgetc(fp)) != EOF){
/*14*/     putchar(ch);
/*15*/   }
/*16*/   fclose(fp);
/*17*/ 
/*18*/   return 0;
/*19*/ }

(untitled)

while 文でfgetcという関数が使われていますね。使い方は、引数にファイルポインタを書くだけです。すると fgetc 関数は、現在ファイルポインタのある位置から1文字読みこんで戻り値として返し、ファイルポインタの位置を1つ進めておきます。また、この関数は、ファイルポインタがファイルの終わりにあるとEOFを返します。EOF はend of fileの略で、ファイルの終わりを意味する定数です。そこで、この性質を利用して例のような while 文を作れば、ファイルの内容を一通り表示させることができます。この他、ファイルからの読み込みには以下のような関数が用意されています。

fscanf(fp, scanf と同じ);    /*scanf のように読みこむ*/
fgets(配列, n, fp);    /*最大 n-1 文字の文字列を配列に格納*/
    /*「fp」はファイルポインタ*/

fscanf は「名前 年齢 得点」のように、決まった書式のデータを複数読み込むのに便利でしょう。また、ファイルの終わりに来ると、fscanf は「EOF」を、fgets は「NULL」を返します。


読み込み中・・・
10秒待っても表示が変わらない場合、次の理由が考えられます。
・Javascript が無効になっています。
・検索エンジンのキャッシュを見ています。
サイトホームへ / 上位ページへ / ページトップへ / PAROFトレンドショッピングへ
Copyright (C) 2010 totobon all right reserved.