第7回:ファイル (11/10)

今週は,数値や文字データをプログラムに読み込んだり出力したりする際に必要となる「ファイルの入出力」を行う.


第6回演習の解答例


ファイルとは

そもそもWindows,UNIXなどの「オペレーティングシステム」では, あるまとまりをもった数値データや文字データ,画像,動画データの集まりを「ファイル」という単位で管理しており,すべてのファイルには識別のために「ファイル名」がつけられている.

計算結果や画像データ,文章など,さまざまな情報は外部記憶装置(ハードディスクやフロッピーディスク,USBメモリ,CD,DVD等のこと)に「ファイル」として記録しておけば,後で読み出したり,別のコンピュータに持っていくことができる.

たとえば,これまで作ってきたプログラムファイルは「C言語ソースファイル」であり,コンパイルされた.exe形式のファイルは「実行ファイル」と呼ばれる. その他,ディジタルカメラで撮影された「画像ファイル」,「動画ファイル」,今閲覧しているこの演習用ホームページも「ファイル」としてWWWサーバー内に保存されている.

コンピュータのディスク上においては,ファイルはメモリと同様,「0」と「1」の並で表されているが,ファイルには,その性質により大きく分けて二種類あり,それぞれ

テキストファイル
C言語のソースファイルや,ホームページのファイルなど,人間が中身を「文字」として読むことのできるファイル
バイナリファイル
プログラムの実行ファイルや,画像ファイル音楽のファイルなど,人間が「文字」として読めないファイル

と呼ばれている.

プログラム内の変数や配列など,何らかの情報を「ファイル」として保存しておく事を「ファイル出力」または「ファイルに書き出す」と言い,逆にファイルに蓄えられている情報をプログラム内の変数や配列に読み込むことを「ファイル入力」または「ファイルから読み込む」と言う.

ファイル入力とファイル出力をあわせて,ファイル入出力と呼び,これら一連の処理をファイル操作と呼ぶ.


ファイル操作の手順

プログラム中でファイルを扱うときの基本的な流れは

  1. ファイルを開く(オープンと言う).
  2. ファイルから情報を変数に読み込む,またはファイルに変数の内容を書き出す.
  3. ファイルを閉じる(クローズと言う).

である.

ファイル操作プログラム例


#include <stdio.h>

void main()
{
    char filename[100];    /*  ファイル名のための文字列  */
    FILE *fp;  /*  0:ファイルポインタを宣言  */

    printf("作成するファイル名を入力してください : ");  /*  これは画面への出力  */
    scanf("%s", filename);                       /*  これはキーボードからの入力  */

    fp = fopen(filename, "w");       /*  1:ファイルを開く(オープン)  */

    fprintf(fp, "test ¥n");             /*  2:ファイルに文字列を書き出す  */
    fprintf(fp, "%d ¥n", 10);           /*  2:ファイルに整数を書き出す  */
    fprintf(fp, "%f ¥n", 3.1415);       /*  2:ファイルに実数を書き出す  */

    fclose(fp);                      /*  3:ファイルを閉じる(クローズ)  */
}

ファイル操作を順に見ていきましょう.

0:ファイルポインタの宣言

ファイルを扱う場合には,必ずファイルポインタを宣言する.
これは,ファイルはディスク上に複数あるため,どのファイルを扱うのか,そのファイルのどの位置を読み書きしているのかを識別・記憶するためである.

書式:FILE *ファイルポインタ変数名;

たとえば,fp と言う名前のファイルポインタを使いたい場合は,

FILE *fp;

と書く.(FILEは構造体であり,ここではfpFILE 構造体変数を指すポインタという意味になる.)

1:ファイルを開く

あるファイルに読み・書きするためには,まず fopen 関数を使って,ファイルをオープンする必要がある.

書式:ファイルポインタ変数名 = fopen ( "ファイル名","モード");

第1引数はファイル名(文字列型),第2引数は「読み取り」と「書き出し」のモード指定(後述)である.

fopen 関数が成功しファイルが開かれると,開いたファイルの先頭位置(ファイルポインタ)が戻り値として fp に代入される.

何らかの理由でファイルのオープンに失敗すると (ファイルが存在しない,別のプログラムがファイルを開いている,ファイル名間違い等),fopen 関数は NULL を返す.
このため,実際のプログラムにおいては,通常は以下のように if 文を用いたエラーチェックを行う.


char filename[] = "data.txt";      /*  ファイル名  */
fp = fopen(filename, "r");  /*  読み取りモードの場合  */
if (fp == NULL) {
    exit(1);                /* exit関数は,ここでプログラムを終了させる関数.要 #include<stdlib.h> */
}   
/* ここ以降にファイルの読み書きを行う */

ファイルが開けなかった場合は,その続きの読み書き処理ができないので,上の例のようにプログラムの実行を終了させるための exit 関数を使うか,または return 文で関数を終わらせる.

モードについて

fopen 関数の第2引数のモードについては以下のとおり.文字列型なので,必ず" "で括る必要がある.

モード説明
"r"読み込み専用で開く.(read)
"w"書き込み専用で開く.(write)
"rw"読み取りと書き込み両方で開く.(read and write)
"a"追加書き込みモードで開く.(append)
すでにあるファイルの末尾に追加.もしファイルがなければ新規作成する

2:ファイルに情報を書き出す,ファイルから情報を読み込む

ファイル入出力では画面からデータを読み書きするのに用いた,scanf, printf 関数によく似た,fscanf, fprintf関数をそれぞれ用いる.

書式(ファイル読み込み):fscanf(ファイルポインタ, "書式指定子", 引数, ...);
書式(ファイル書き出し):fprintf(ファイルポインタ, "書式指定子", 引数, ...);

使い方は,scanf, printf 関数と同じである.
違いは第1引数にファイルポインタを渡す点である.(どのファイルに読み書きするかを識別するため)

fprinf 関数の使用法は,画面出力のprintfとほぼ同じである.

例1:ファイルに実数を書き出す例.(注:以下の4例では,FILE* fp; や,fopen など必要な処理が省略されている.)


fprintf(fp, "%f ¥n", 3.14159265);

例2:ファイルに文字列を書き出す例.


char[] str = "Meiji University";

fprintf(fp, "%s ¥n", str);

fscanf 関数は,ファイル中の「スペース記号」または「改行記号」を区切りとしてファイルの内容を読み取り,変数に値を代入する.

例3:ファイルに書かれた数値 (例えば「1000」など) を,整数型変数に読み込む例.


int i;
fscanf(fp, "%d", &i);   /*  &を忘れないこと.アドレスを取り出す演算子  */

例4:ファイルに書かれた文字列 (例えば「Ikuta」など) を,文字列型変数に読み込む例.


char mojiretsu[100];
fscanf(fp, "%s", mojiretsu);   /*  & は不要!  */

3:ファイルを閉じる

ファイルの利用が終わったら,プログラムを終了する前に必ずファイルをクローズする.

書式:fclose(ファイルポインタ);

例:fclose(fp);

これを忘れると,ファイルの書き出しが完了しない場合がある.


まとめ

以下に,ファイル入出力と,キーボード・画面入出力の比較を示す.

画面出力ファイル出力

#include <stdio.h>

void main()
{










    printf("test");

}

#include <stdio.h>

void main()
{
    char filename[] = "test.txt";   /*  ファイル名は任意  */
    FILE *fp;

    fp = fopen(filename, "w");
   
    if(fp == NULL){
        printf("ファイルを作れませんでした");
        return;  /*  ここでプログラム終了  */
    }

    fprintf(fp, "test");
    fclose(fp);
}
キーボード入力ファイル入力

#include <stdio.h>

void main()
{
    char s[100];











    scanf("%s", s);
    printf("%s", s);

}

#include <stdio.h>

void main()
{
    char s[100];
    char filename[] = "test.txt";   /*  ファイル名は任意  */
   
    FILE *fp;
   
    fp = fopen(filename, "r");
   
    if(fp == NULL){
        printf("ファイルがありませんでした");
        return;  /*  ここでプログラム終了  */
    }

    fscanf(fp, "%s", s);
    printf("%s", s);
    fclose(fp);
}

実際にやってみて確認してみよう.


演習課題

(1)scanfを使ってキーボードから入力した数値をファイルに保存するプログラムを作成せよ.

(2)上問で作ったファイルに保存された数値を読み込み,画面に表示するプログラムを作成せよ.

(3)角度を0度から360度まで1度ずつ変えたときの,sinの値を各行に出力するプログラムを作成せよ.計算上,円周率はpi=3.141とする.

ファイル名は,"sin.csv"としよう.ファイル名の末尾に .csv (拡張子と呼ぶ)をつけると,Excelで簡単に開くことができる.

sin関数の使い方は,以下のプログラムを参考にすること


#include <stdio.h>		/*printfなどの命令を使うためのファイルの読み込み*/
#include <math.h>		/*sin関数など数学用の関数を使うためには必ず読み込むこと*/

void main()
{
	double rad,pi;		/*sin(double x)のため,変数xはdouble型で定義すること*/
	double x;
	
	pi=3.141;			/*πの定義*/
	rad = 10.0/180.0*pi;	/*sin関数の引数は,ラジアン[rad]にすること*/
	
	
	x=sin(rad);
	
	printf("角度10°の時のsinの値は%1.3fです。¥n",x);
	
}

結果の例: 

0.000000
0.017452
0.034898
....

(4)(3)を利用して,角度を0度から360度まで1度ずつ変えたときの,sinの値,cosの値,tanの値を各列に出力するプログラムを作成せよ.

授業終了時までに完成したプログラムをoh-meijiシステムを使って提出すること.
(提出期限を厳守し,提出用の回に提出しないと採点を行わない)