ファイルの概念

これまでデータの入力はキーボードから行ってきた。しかし、入力データが大量になると、その作業は大変なものとなる。そこで、Cではあらかじめファイルに入っているデータの入出力ができるように、fopen,fscanf,fprintf,fcloseなどの関数が用意されている。

 

関数名

機能

一般形

fopen

x.c y.datなどの外部名を受け取り、ファイルを読み書きするのに必要なポインタを返す。

FILE *fp;

 

fp = fopen(name,mode);

fscanf

fopenで開いたファイルのデータを1文字列ずつ読み込む

int   s;

 

fscanf(fp,”%d”,&s);

fprintf

fopenで開いたファイルのデータへ書き込み

FILE *fp;

int   s;

 

fprintf(fp, “%d”,s);

fclose

fopenで開いたファイルを閉じる。

FILE *fp;

 

fclose(fp);

feof

データが空かどうかの判定を行う。空ならば真を返す。

FILE *fp;

 

feof(fp);

fgets

1行取り込む

fgets(fp,80,s)で配列sに1行取り込まれる。

fgets1回実行すると、fpidが次の行の先頭に移る。

 

char s[80];

FILE *fp;

 

fgets(fp,80,s)

fgetc

1文字取り込む

 

 

FILE *fp;

 

fgtec(fp);

fseek

ファイル位置の移動

offsetには先頭からの文字数が入る。

originには0,1,2が入る。0で始め、1で現在地、2で最後

FILE *fp;

 

fseek(fp,offset,origin);

fflush

ファイルを閉じずに書き込み

Fflush(fp);

ftell

ファイルポジションインディケータ(fpid)の位置を調べる

fgets1回実行すると、2行目の先頭にfpidが移動。

 

rewind

ファイルの先頭に移る。

 

fopen(name,mode)nameはファイル名を表し、modeにはr,w,a,r+,w+,a+があり、rread(読み込み)wwrite(書き込み)、aappend(ファイルの終わりに追加書き込み)を表す。また、r+はファイルの読み書き、w+は今までのファイルを破棄して読み書き、a+はファイルの追加・変更を表す。

 

fopen,fscanf,fprintf,fcloseの使い方

 

ファイルからの入力

これらの関数を使ってdata.datという名前のファイルの操作をしてみよう。

 

例題10.1 入力ファイルからデータを読み込む

次のようなデータを持ったファイルdata.datC\tempにあるとき、data.datからデータを読み込み、平均と和を求めるプログラムを作成しよう。

データ 

150

164

153

174

189

185

168

156

152

174

173

169

 

実行結果

          

考え方

fopenを使ってdata.datファイルを開き、feofでデータが空かチェックし、fscanfを用いて1つずつデータを最後まで読み込む。data.datC:\tempにあるのでダブルクォートの中では、”C:\\temp\\data.dat”と書く必要がある。いったんファイルを開いてしまえば、fscanfscanfと同じ使い方になる。平均と合計を計算したら、最後にfcloseでファイルを閉じる。

 

解答

#include <stdio.h>

#include <stdlib.h>

 

void main(void)

{

             int value=1;

             int sum=0;

             int count=0;

             FILE *fpin;

 

             if( (fpin=fopen("C:\\temp\\data.dat","r")) == NULL)

             {

                           printf("ファイルが見つかりません。---data.dat\n");

                           exit(1);

             }

             while( !feof(fpin) ) // 最後まで

             {

                           fscanf(fpin,"%d",&value);

                           sum = sum + value;

                           count++;

             }

             if (count > 0)

             {

                           printf("合計=%d 平均値=%f\n",sum,(double)sum/count);

             }

             fclose(fpin);

}

読み込みのときは、読み込みであることが後からすぐに分かるように、fpinとしておくと便利である。

 

例題10.2 ファイル名を指定した入力

プログラムを工夫することによって、データファイルの名前をキーボードから直接入力するプログラムを作成せよ。

実行結果

          

解答 fopenで指定したファイル名の要素は、文字配列でも指定できる。例えば、

              char  FileName[16]:

              printf(“ファイル名を入力してください”);

              scanf(“%s”,FileName);

とすればよい。ではプログラムを書いてみよう。

 

#include <stdio.h>

#include <stdlib.h>

 

void main(void)

{

             int value=1;

             int sum=0;

             int count=0;

             char FileName[16];

             FILE *fpin;

 

             printf("ファイル名を入力してください");

             scanf("%s",FileName);

 

             if( (fpin=fopen("data.dat","r")) == NULL)

             {

                           printf("ファイルが見つかりません。---data.dat\n");

                           exit(1);

             }

             while( !feof(fpin) )

             {

                           fscanf(fpin,"%d",&value);

                           sum = sum + value;

                           count++;

             }

             if (count > 0)

             {

                           printf("合計=%d 平均値=%f\n",sum,(double)sum/count);

             }

             fclose(fpin);

}

 

ここでは、1つのデータファイルdata.datからデータを読み込むことを行ったが、複数個のデータファイルを読み込むには

              FILE *fpin1, *fpin2, *fpin3;

と宣言し、それぞれのファイルをfopenで開けばよい。

 

ファイルへの書き込み

ファイルへデータを出力するには、

1.まずout.datという名前のファイルを作成する。書きこみのときはfpoutとしておく。

              FILE *fpout;

              fpout = fopen(“out.dat”,”w”);

このとき、fopenの第2引数modeは書き込み可能とするためになる。ただし、”w”を指定してfopenを行った場合、out.datが存在するとout.datのデータ内容は失われるので注意すること。つまり、”w”は上書きを意味する。

2.fprintf関数を用いてファイルにデータを出力する。

              fprintf(fpout,”%d\n”,value);

3.最後にファイルを閉じる。

              fclose(fpout);

 

例題10.3 次のデータをout.datというファイルを作成し、書き込むプログラムを作成しよう。

データ 

150

164

153

174

189

185

168

156

152

174

173

169

 

考え方

1.まずout.datというファイルを作成。

2.キーボードからのデータをscanfで読み込み、fprintfout.datに書き込む

解答

#include <stdio.h>

#include <stdlib.h>

 

void main(void)

{

             int value=1;

             FILE *fpout;

 

             if( (fpout = fopen("out.dat","w")) == NULL)

             {

                           fclose(fpout);

                           printf("ファイルが作成できません。---out.dat\n");

                           exit(1);

             }

            

             while( value != 0 )

             {

                           printf("データを入力してください。終了するには0を入力\n");

                           scanf("%d",&value);

                           fprintf(fpout,"%d\n",value);

             }

             fclose(fpout);

}

 

ファイルへの追加書き込み

ここでは、すでに作成してあるファイルに新しいデータを追加する方法を考える。fopenmodeaにすると追加書き込みができるようになる。このとき新しく追加したデータは元のファイルの下に追加されます。

 

例題10.4 例題10.3で作成したout.datに次のデータを追加してみよう。

データ

160

170

180

175

 

解答

#include <stdio.h>

#include <stdlib.h>

 

void main(void)

{

             int value=1;

             FILE *fpout;

 

             if( (fpout = fopen("out.dat","a")) == NULL)

             {

                           fclose(fpout);

                           printf("ファイルが作成できません。---out.dat\n");

                           exit(1);

             }

            

             while(value != 0)

             {

                           printf("データを入力してください。終了するには0を入力\n");

                           scanf("%d",&value);

                           fprintf(fpout,"%d\n",value);

             }

             fclose(fpout);

}

 

ファイルの文字変更(fseekの使いかた)

fseek(fp,offset,origin)originに対してファイルのoffsetの位置に移動。origin0,1,2が入る、0は先頭、1は現在地、3はファイルの最後。offsetには0L,1L,2L,…が入る。0Lで1文字目、2Lで2文字目に移動する。

fseek(fp,0L,0)で開始行の1文字目に移動する。

 

次のような文字列があるデータファイルをtest.datと名前を付けて保存しておく。

D is defficalt.

 

1.文字DCに変える。

#include <stdio.h>

#include <stdlib.h>

 

void main(void)

{

             char c;

             char s[80];

             FILE *fpout;

 

             if( (fpout = fopen("test.dat","r+")) == NULL)

             {

                           fclose(fpout);

                           printf("ファイルが作成できません。---test.dat\n");

                           exit(1);

             }

 

             fgets(s,80,fpout);

             fseek(fpout,0L,0);

             c = 'C';

             fprintf(fpout,"%c",c);

             fclose(fpout);

}

で文字DCに置き換えられる。

2.C is defficalteiに変える。

#include <stdio.h>

#include <stdlib.h>

 

void main(void)

{

              int i;

              int j;

              char c;

              char s[80];

              FILE *fpout;

 

              if( (fpout = fopen("test.dat","r+")) == NULL)

              {

                            fclose(fpout);

                            printf("ファイルが作成できません。---test.dat\n");

                            exit(1);

              }

 

             fgets(s,80,fpout);

             for(i=0;i<80;i++)

             {

                           if(s[i] == 'e')

                           {

                           j = (long)i;

                           fseek(fpout,j,0);

                           c = 'i';

                           fprintf(fpout,"%c",c);

                           }

             }

             fclose(fpout);

}

3.C is difficaltのスペルミスを見つけて修正する。

fseekfprintfはペアで用いる。つまり、fseekfprintfの間には他の操作を行ってはいけない。他の操作を行うとfseekfpが変わってしまう。

 

#include <stdio.h>

#include <stdlib.h>

 

void main(void)

{

             int i;

             int j;

             char ch,ad;

             char s[80];

             FILE *fpout;

 

             if( (fpout = fopen("test.dat","r+")) == NULL)

             {

                           fclose(fpout);

                           printf("ファイルが作成できません。---test.dat\n");

                           exit(1);

             }

 

             fgets(s,80,fpout);

             printf("%s",&s[0]);

             printf("修正したい文字と修正用の文字を入力\n");

             scanf("%c %c",&ch,&ad);

 

             for(i=0;i<80;i++)

             {

                           if(s[i] == ch)

                           {

                           j = (long)i;

                           fseek(fpout,j,0);

                           fprintf(fpout,"%c",ad);

                           }

             }

             fclose(fpout);

}

 

ファイルの追加変更(文字列)

文字列を読み込むにはfgets(s,80,fpout);を用いる。

次の文字列を含んだデータファイルtest.datを用意する。このファイルをプロンプトから開き、文字列を表示し修正したい文字列を入力することにより、ファイルが書き直されるプログラムを作成しよう。

They is stupid.

文字列の長さはstrlen(s)で計る。

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

void main(void)

{

             int i;

             int j;

             int len,len1,len2;

             char ch[5],ad[5];

             char s[80];

             FILE *fpout;

 

             if( (fpout = fopen("test.dat","r+")) == NULL)

             {

                           fclose(fpout);

                           printf("ファイルが作成できません。---test.dat\n");

                           exit(1);

             }

                           fgets(s,80,fpout);

 

                           printf("%s",&s[0]);

                           len=strlen(s);

                           printf("修正したい文字列と修正用の文字列を入力\n");

                           scanf("%s %s",ch,ad);

                           len1=strlen(ch);

                           len2=strlen(ad);

 

                           for(i=0;i<80;i++)

                           {

                                         if(s[i] == ch[0])

                                         {

                                         j = (long)i;

                                         fseek(fpout,j,0);

                                         fgets(s,40,fpout);

                                         fseek(fpout,j+len2-len1,0);

                                         fprintf(fpout,"%s",s);

                                         fseek(fpout,j,0);

                                         fprintf(fpout,"%s",ad);

                                         break;

                                         }

                           }

             fclose(fpout);

}

 

リダイレクション

Cにはリダイレクションと呼ばれ、入出力の切り替えを行う機能がある。つまり、キーボードやディスプレイもファイルと考えると、ファイルから読み込むことも、ファイルへの出力もキーボードからの入力やディスプレイへの出力と同じと考えることができる。では、実際にリダイレクションを行ってみる。

 

1. 次のプログラムをread_data.cというファイル名でコンパイルしよう。

#include <stdio.h>

 

void main(void)

{

             char  s[80];

            

             while(gets(s) !=NULL))

             {

             puts(s);

             }

}

 

2. 例題10.1で用いた入力ファイルdata.datをリダイレクションを用いて読み込んでみよう。

              read_data < data.dat

とプロンプトで実行する。これは、data.datread_data.exeは同じフォルダにあることを前提にしている。もしdata.datが別の場所にあるときは、パス(path)が必要になる。パスとは目的地までの道順のことである。

 

例えば、read.datCドライブのフォルダMy Documents の下のフォルダCEXEにあり、read_dataCドライブのフォルダDATにあるとする。この場合は次のように入力すればよい。

              cd c:\mydocu~1\CEXE

              read_data < ..\data.dat

これは相対パスと呼ばれるものである。この他絶対パスを用いると次のようになる。

              read_data < C:\DAT\data.dat

 

 

3. 例題10.3で行ったファイルへの書き込みをリダイレクションを用いて行ってみよう。書き込みするファイル名はout.txtとする。

              read_data > out.txt

この後、type out.txtとプロンプトで入力するとout.txtの内容が見れる。UNIXの場合はcat out.txtと入力。

 

4. 例題10.4で行ったファイルへの追加書き込みをリダイレクションを用いて行ってみよう。追加書き込みするファイル名はout.txtとする。

              read_data >> out.txt

この後、type out.txtとプロンプトで入力するとout.txtの内容が見れる。UNIXの場合はcat out.txtと入力。

 

5. 読み込みと書き込みをリダイレクションで行ってみよう。

              read_data < data.dat > data.out

この操作を行うと、まずdata.datの内容がよき込まれ、次にdata.outに書き込まれる。

 

練習問題10.1

次のプログラムの説明およびプログラムを読んで、設問に答えよ。

「プログラムの説明」

ソフトウェア開発のテスト工程におけるバグ収束状況を評価するための信頼度成長曲線を印刷するプログラムである。信頼度成長曲線は、テスト項目の消化状況とバグの発生状況との関係をグラフにしたものである。

(1) テスト担当者が日々記録しているテスト項目の消化項目数(その日に終了したテストの項目数)と摘出バグ数(終了した項目において摘出したバグ数)の一覧が、下のバグ記録表である。

バグ記録表

日付

担当

消化項目数

摘出バグ数

19960802

A

2

40

19960803

B

1

20

19960804

A

3

20

19960805

B

4

10

 

 

 

 

19960823

A

10

5

19960823

C

5

0

19960825

A

8

1

19960827

C

12

1

(2)       バグ記録表は、下の図のレコード様式でbugrec.txtというファイルに格納されている。

8けた

1けた

1けた

1けた

2けた

1けた

2けた

1けた

日付

担当

消化項目数

摘出バグ数

\n

     :空白文字

 

1.              日付は、年月日を8桁の10進数字(YYYYMMDD)で表現しており、先頭の4桁が西暦年を、次の2桁が月を、最後の2桁が日を表している。

2.              担当は、テスト担当を1桁の英字にコード化したものである。

3.              消化項目数および摘出バグ数は、2桁の10進数字である。

4.              各項目は、1個の空白文字で区切られており、レコードの終端は改行文字”\n”である。

5.              レコードはテスト担当者ごとに1日のテスト結果を記録したものであり、同一日付でテスト担当者が異なるレコードが2件以上存在することもある。

6.              レコードは日付の昇順に整列されている。

7.              ファイルbugrec.txtに記録されているテスト作業日の数は60日以内である。

(3) このプログラムでは、ファイルbugrec.txtを入力し、1日あたりの消化項目数および摘出バぐ数を集計する。さらに、その日までの累積数を計算して、累積消化項目数と累積摘出バグ数の関係をもとめ、折れ線グラフ出力関数prtgraphを用いて信頼度成長曲線を印刷する。

(4) 関数prtgraphの仕様は、次のとおりである。

   「機能」 第1引数で指定された個数の点(座標は第2引数の配列内に格納)を折れ線でつないで印刷する。

   「書式」 

                            typedef struct{int x;int y;} ZAHYO;

                            void prtgraph(int dcnt,ZAHYO +tbl, char *title);

 

   「引数」 構造体ZAHYOのメンバx:印刷する点のX座標

                            構造体ZAHYOのメンバy:印刷する点のY座標

                           

                            dcnt: 印刷する点の個数

                            tbl:  点の座標を格納した配列

                            title: グラフの表題

 

<プログラム>

#include <stdio.h>

 

#define STBMAX 61

 

typedef struct{int x;int y;} ZAHYO;

void prtgraph(int,ZAHYO,char*);

 

void main(void)

{

              ZAHYO  s_tbl[STBMAX];

              FILE *fp;

              long yymmdd, t_yymmdd;

              int t_bug, t_chk,I;

              int idx=0;

              char tanto;

 

              fp = fopen(“bugrec.txt”,”r”);

              yymmdd=0;

              s_tbl[0].x = s_tbl{0},y = 0;

              while(fscanf(fp,”%81d %c %2d %2d\n”,

                                          &t_yymmdd, &tanto, &t_chk, &t_bug) != EOF)

              {

                            if (yymmdd != t_yymmdd)

                            {

                                          idx++;

                                          yymmdd = t_yymmdd;

                                          s_tbl[idx].x = s_tbl[idx].y = 0;

                            }

                            s_

 


C入門