ログイン
kid's world

ファイルへの書き込み

ファイルへの書き込み

それでは,実際にファイルを扱ってみよう。まずは比較的簡単なファイルへの書き込みから始めることにする。ファイルにデータを書き込むには,おおまかにいって次のような順序を必ずたどる。

1.ファイルを開く
2.データの書き込み
3.ファイルを閉じる

いきなりファイルに対してデータの書き込みができるわけではない。どのファイルに対して書き込めばよいのか,どのような形式で書き込めばよいのかなどがわからなければ,書き込みたくても書き込みようがないからだ。それら書き込みのために必要な準備を行うのが,1.の「ファイルを開く」の役割である。

ファイルが無事に開けたら,いよいよ2.の「データの書き込み」である。データの書き込みは,いろいろな関数で行うことができるが,ほとんどの関数は画面に対して文字を出力するのに使ってきた関数とほぼ同じ書式になっている。その理由は,画面も1つの特殊なファイルとして扱われているからなのだ。出力先がディスクではなく,画面になっていただけのことなのである。これについては後ほど解説しよう。

ファイルへのデータの書き込みが終わったら,次に,3.の「ファイルを閉じる」操作が必要である。これを怠ってしまうと,せっかく書き込んだデータが消えてしまう場合がある。というのは,とくにハードディスクに書き込む場合,すぐにデータが書き込まれるわけではない。1バイトずつ細切れに書き込んでいくよりも,大量のデータをまとめて一度に書き込んだほうが速いからである。したがって,ほとんどのOSでは,ある程度データをメモリ上にためてから,一気に書き込むということをしている。もしファイルを閉じる操作をしないと,データが書き込まれずにメモリ上に残ったままということが起こる場合があるのだ。ほかにもさまざまなトラブルが起こる。したがって,ファイルへの書き込みが終了したら,必ず「ファイルを閉じる」操作をしなければならない。

では,以下で具体的にそれぞれの操作について解説していく。

ファイルを開く

ご存じのとおり,ファイルには「ファイル名」という,ほかのファイルと混同しないために用いられる識別子がある。すなわち,ファイルにアクセスしようとしたときは,必ずファイル名を指定することになるわけだが,もちろんそれはC言語でも同じことである。ファイル名の指定は実際に読み書きするときではなく,ファイルを開くときに指定する。ファイル名の指定と同時に,そのファイルは書き込み用に開かれるのか,読み込み用に開かれるのか,または読み書き両用に開かれるのか,なども指定する。

具体的には,ファイルを開くにはfopen関数を使う。この関数は,次のような形式になっている。

FILE *fopen(const char *filename, const char *mode)

第1引数のfilenameには,これから書き込もうとしているファイルのファイル名を指定する。そして,第2引数のmodeには,どのようなモードでファイルを開くかを指定する。テキストファイルを扱う場合,モードにはTable 1に示す文字列が指定できる。ここでは書き込み用にテキストファイルを作成するので,引数には"w"を指定する。なお,メモリ上のデータをそのまま書き込むバイナリファイルの扱い方についてはあとで述べる。

Table 1: fopen関数のmode引数

モード意味
"r"読み出し用にファイルを開く。ファイルが存在しない場合や見つからない場合は失敗する。
"w"書き込み用にファイルを作成、ファイルが存在する場合はそのファイルの内容は破棄する。
"a"追加書き込みのために既存のファイルを開く。ファイルが存在しない場合は作成する。
"r+"読み書き用に開く。ファイルは存在していなければならない。
"w+"読み書き用にファイルを作成。ファイルが存在する場合はそのファイルの内容は破棄する。
"a+"読み出しと追加の両方のモードでファイルを開く。ファイルが存在しない場合は作成する。

さて,このfopen関数は,「FILE」という見慣れない型へのポインタを返しているが,これはファイルポインタと呼ばれるポインタで,開いたファイルへのポインタだとイメージしていただいてけっこうである(Fig.1)。ファイルの読み書きには,そのつどファイル名を指定するのではなく,このときに得たファイルポインタを介してアクセスすることになる。

Fig.1

/c/file-fig1.png

いったんファイル名を指定してファイルを開けば,あとは関連づけられたファイルポインタのみを取り扱えばよい。ファイルは複数同時に開いて処理を行う場合もあるが,いくつかのファイルポインタを宣言しておけば,複数のファイルを同時に扱うことが容易にできる。

ファイルを開くには,具体的には以下のようにする。

FILE *fp;
fp=fopen("file.txt","w");

この例では,まずファイルポインタfpを宣言し,fopen関数を呼び出している。このfopen関数は「file.txt」ファイルを書き込み用に作成し,そのファイルへのファイルポインタをfpへ代入している。

エラーの処理

何らかの理由でファイルが開けなかったとき,fopen関数はNULLを返す。もしファイルが開けなかったときにプログラムを終了したいときは,List 1のように書くとよい。List 1では新たに登場したexit関数が使われているが,直ちにプログラムを終了するための関数である。引数に1が指定されているが,これはプログラムの呼び出し元に渡される値であり,正常に終了したときは0,異常終了したときはそれ以外の値を返すことが慣例となっている。ここでは,ファイルを開けず終了したときに1を返すようにしている。なお,exit関数はstdlib.hをインクルードして利用する。

List 1: ファイルが開けなかったときの処理例

#include <stdio.h>
#include <stdlib.h>

main()
{
  FILE *fp;

  if((fp=fopen("file.txt","w")) == NULL) {
    printf("ファイルが開けませんでした。\n");
    exit(1);
  }
  
  (以下、ファイルの書き込み処理)

}

また,エラーメッセージをprintf関数の代わりにperror関数を使って表示させると,実際にどのようなエラーが発生したのかが同時に表示されるので便利である。たとえばList 2のように、存在しないファイル名(これはファイル名ではなくてディレクトリ名である)を指定すると、Fig. 2のように、指定したメッセージとともにエラーメッセージが表示される。

List 2: perror関数の使用例

#include <stdio.h>
#include <stdlib.h>

main()
{
  FILE *fp;
  
  if ((fp=fopen("c:\\", "w")) == NULL) {
    perror("ファイルが開けません");
    exit(1);
  }
  
  fclose(fp);
}

Fig.2: perror関数によるエラーメッセージ

/c/file-fig2.png

ファイル名に関する注意

指定するファイル名についてだが,少し気をつけなければならないことがある。たとえば,MS-DOSやWindowsのシステム上で,Cドライブのtxtディレクトリに,「test.txt」という名前のファイルを書き込み用に作成しようとして,

fp=fopen("c:\txt\test.txt","w");

と書いたとしよう。一見なんの不都合がないように見えるが,実は「\t」の部分が,以前紹介したエスケープ文字に相当するため,タブ文字に変換されてしまうので期待どおりに処理されない。この場合は「\」の代わりに「\\」と書かなければならない。

fp=fopen("c:\\txt\\test.txt","w");

これで,無事にファイルが作成される。

ファイルに書き込む

ファイルにデータを書き込むための関数にはいろいろあるが,もっとも使いやすいのはfprintf関数であろう。これまでさまざまな場面で使い込んできたprintf関数の名前の先頭に「f」がついたものだ。使い方もprintf関数とほとんど同じで,異なるのは引数の先頭にファイルポインタが必要となることだけである。

たとえば,これまで,

printf("iの値は%dです。\n", i);

などと書いて変数の内容を画面に表示してきたが,その内容をそのままテキストファイルに書き出すには,引数の先頭にファイルポインタを追加して,

fprintf(fp, "iの値は%dです。\n", i);

としてやるだけである。これで,ファイルポインタfpに関連づけられたファイルに文字列が書き込まれる。

ファイルへの書き込みが終わったら,ファイルは必ず閉じなければならない。ファイルを閉じる方法はいたって簡単だ。ファイルを開いたときに得たファイルポインタを,fclose関数に引数として渡すだけである。

fclose(fp);

この操作によって,すべてのデータはファイルに完全な形として書き込まれる。

ファイルをエディタで開いてみる

では,プログラムを実行してできあがったファイルをエディタで開いてみて,正しく書き込めたかを確認してみよう。

前々回に日数を返すプログラムを作ったが,これを改造して,2000年から2010年までの各月の日数をファイルに書き込むプログラムを作ることにする。List 3にそのプログラムを示す。

List 3: 各月の日数を書き出す

#include <stdio.h>
#include <stdlib.h>

main()
{
  int y,m,d;
  FILE *fp;
  
  if ((fp=fopen("days.txt","w")) == NULL) {
    perror("ファイルを開けません");
    exit(1);
  }
  
  for(y=2000; y<=2010; y++){
    for(m=1; m<=12; m++){
      
      switch(m){
      case 1:
      case 3:
      case 5:
      case 7:
      case 8:
      case 10:
      case 12:
        d=31;
        break;
      case 4:
      case 6:
      case 9:
      case 11:
        d=30;
        break;
    case 2:
      if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
        day=29;
      else
        day=28;
      }
      
      fprintf(fp, "%d年%d月は%d日間あります。\n", y,m,d);
    }
  }
  fclose(fp);
}

これまで説明してきたように,ファイルを開き,データを書き込み,ファイルを閉じるという順番でファイルを扱う。まずfopen関数でファイルを書き込み用に作成する。次にデータの書き込みを行う。for文を2つ使って,変数yを2000から2010まで1つずつ増やす間に,変数mを1から12まで増やし,fprintf関数を使って「y年m月」の日数をファイルに書き込む。for文を抜けたあとは,fclose関数を使ってファイルを閉じる。

List 3を動かしてみよう。無事にファイルに書き込まれているだろうか。Fig. 3は,そのファイルをWindowsのメモ帳で読み込んだ例である。C言語では,このようにしてファイルを書き込むことができる。

Fig.3: 作成されたテキストファイルの内容

/c/file-fig3.png


ファイルからの読み込み」へ進む

広告


©Toshio Koide 1996-2007.

目次

リンクについて

リンクは御自由にどうぞ。

メール

mail.gif

広告