C言語 - ファイル操作5

TOPプログラムの話>C言語

それではfprintfとfscanfを使って実践的なことをやってみましょう。
ファイルを読み込み・書き込みに強くなると、大きなプログラムを組むとき強い戦力になります。(少なくとも私はそう思っています。)
たとえば 画像ファイルを読み込むときファイルの名前と大きさをプログラムが理解しなければ読み込めませんよね?
けれど、たくさんの画像ファイルがある場合、一々ファイル名とその大きさを指定していくのは面倒です。
ファイル名とその大きさ一覧表をテキストファイルで作って、プログラムはそれを読み込んでその一覧にある画像ファイルを全て読み込む、という方法をとった方がかなり楽です。
(修正も楽ですし、一覧表があるというのはわかりやすくていいです。)
それを想定してファイル操作をしてみましょう。

この章の最終目標
必要な画像ごとに画像ファイル名、画像ファイルの縦幅、横幅の三つのステータスが書いてある一覧表を読み込むプログラムを作る。

それではまず、ユーザーの気が済むまで画像のステータスを入力させ(scanf)、表示(printf)させてみましょう。
(ある程度力のある人はさらっと見るだけでいいです。)


///////////サンプル1///////////////
///////////file1.c//////////////////

#include <stdio.h>

int main()
{
	char fname[256];
	int x,y;
	int yesno=0;

	while(yesno==0)/*yesnoが0の間繰り返す。*/
	{
		/*画像のステータスを読み込む。*/
		printf("画像ファイル名(拡張子含む)>");
		scanf("%s",fname);
		printf("横幅(px)>");
		scanf("%d",&x);
		printf("縦幅(px)>");
		scanf("%d",&y);

		/*画像のステータスを表示。*/
		printf("\n表示\n");
		printf("画像名 : %s\n",fname);
		printf("横幅   : %d\n",x);
		printf("縦幅   : %d\n",y);
		
		/*続けるかどうかチェック*/
		printf("続けますか?(Yes=0)>");
		scanf("%d",&yesno);
		printf("\n");
	}
	return 0;
}
/////////////////////////////////////////////////////
////////////////////実行結果(例)////////////////////////
画像ファイル名(拡張子含む)>dennezu1.bmp
横幅(px)>100
縦幅(px)>80
dennezu1.bmp 100 80
続けますか?(Yes=0)>0

画像ファイル名(拡張子含む)>basutabu1.bmp
横幅(px)>200
縦幅(px)>400
basutabu1.bmp 200 400
続けますか?(Yes=0)>1

Press any key to continue
////////////////////////////////////////////////////

簡単にできましたね。
それでは次は取得した画像ファイルデータをfprintfを使って"test.txt"というテキストファイルに書き込みましょう。


///////////サンプル2///////////////
///////////file2.c/////////////////////

#include <stdio.h>

int main()
{
	FILE *fp;/*ファイルポインタ*/
	char fname[256];
	int x,y;
	int yesno=0;

	fp=fopen("text.txt","w");
	if(fp==NULL)
	{
		printf("ファイルが読み込めません。\n");
		return 0;
	}
	while(yesno==0)/*yesnoが0の間繰り返す。*/
	{
		/*画像のステータスを読み込む。*/
		printf("画像ファイル名(拡張子含む)>");
		scanf("%s",fname);
		printf("横幅(px)>");
		scanf("%d",&x);
		printf("縦幅(px)>");
		scanf("%d",&y);

		/*画像のステータスを表示。*/
		fprintf(fp,"%s %d %d",fname,x,y);
		
		/*続けるかどうかチェック*/
		printf("続けますか?(Yes=0)>");
		scanf("%d",&yesno);
		fprintf(fp,"\n");
	}
	fclose(fp);
	return 0;
}
//////////////実行結果(例)////////////////////////////
画像ファイル名(拡張子含む)>dennezu1.bmp
横幅(px)>100
縦幅(px)>100
続けますか?(Yes=0)>0

画像ファイル名(拡張子含む)>basutabu.bmp
横幅(px)>210
縦幅(px)>400
続けますか?(Yes=0)>1

Press any key to continue
////////////text.txt////////////////////////////////
dennezu1.bmp 100 100
basutabu.bmp 210 400
////////////////////////////////////////////////////

ここまでは簡単ですね。
]サンプル1のプログラムの表示部分(printf)をfprintfにしたのと、入力が開始する前と後にfopenとfcloseを使ってファイルと開いて閉じてます。

それでは今作ったプログラムで精製されたtext.txtをfscanfで読み込むプログラムを作ってみましょう。


//////サンプル3_1(MISS)////////////////
#include <stdio.h>

int main()
{
	FILE *fp;/*ファイルポインタ*/
	char fname[256];
	int x,y;
	int yesno=0;

	fp=fopen("text.txt","r");
	if(fp==NULL)
	{
		printf("ファイルが読み込めません。\n");
		return 0;
	}
	while(yesno==0)/*yesnoが0の間繰り返す。*/
	{
		/*画像のステータスを読み込む。*/
		fscanf(fp,"%s",fname);
		fscanf(fp,"%d",&x);
		fscanf(fp,"%d",&y);

		/*画像のステータスを表示。*/
		printf("%s %d %d\n",fname,x,y);
		
		/*続けるかどうかチェック*/
		printf("続けますか?(Yes=0)>");
		scanf("%d",&yesno);
		printf("\n");
	}
	fclose(fp);
	return 0;
}
/////////////////////////////////////////////////////////
/////////////実行結果///////////////////////////////////
dennezu1.bmp 100 100
続けますか?(Yes=0)>0

basutabu.bmp 210 400
続けますか?(Yes=0)>1

Press any key to continue
////////////////////////////////////////////////////////

おぉ、上出来上出来・・・・・って違うわ!!
せっかくプログラムに自動的に読み込ませてるんだから、自動的に最後まで読み込めるようにしないと意味ないでしょ!!
もし仮にユーザーがずっと0を入力し続けたら終わらないでしょ!(そんなユーザーはいないだろうが)
もし、そんなことをしたら・・・


//////////実行結果/////////////////////////////////////
dennezu1.bmp 100 100
続けますか?(Yes=0)>0

basutabu.bmp 210 400
続けますか?(Yes=0)>0

basutabu.bmp 210 400
続けますか?(Yes=0)>0

basutabu.bmp 210 400
続けますか?(Yes=0)>0

	・
	・
	・
/////////////////////////////////////////////////////////

って感じになってしまうんです!!

ではどうしたらいいでしょうか?
この場合考えられる方法は

  1. テキストファイルの冒頭に読み込む数を書いておいて、その数だけ読み込むようにする。
  2. 特定の数字または文字列をファイルの一番最後に書いて、それを読み込んだら読み込み終了するようにする。
  3. ファイルが終端に達したかチェックし、終端に達したら読み込み終了する。

などが考えられます。
ぶっちゃけ3が一番てっとり早いですね。
ファイルが終端に達したかどうかチェックするのにfeofという関数を使用します。feopの構造は

int feof(FILE *ファイルポインタ)

のようになっていて 引数に指定したファイルポインタが終端に達していた場合、0以外を返します。(達していなければ0を返します。)
これを利用して次のようにします。


//////サンプル3_2(ほぼ完璧)////////////////

#include <stdio.h>

int main()
{
	FILE *fp;/*ファイルポインタ*/
	char fname[256];
	int x,y;


	fp=fopen("text.txt","r");
	if(fp==NULL)
	{
		printf("ファイルが読み込めません。\n");
		return 0;
	}
	while(feof(fp)==0)/*ファイルが終端に達したら(0以外だったら)読み込み終了*/
	{
		/*画像のステータスを読み込む。*/
		fscanf(fp,"%s",fname);
		fscanf(fp,"%d",&x);
		fscanf(fp,"%d",&y);

		/*画像のステータスを表示。*/
		printf("%s %d %d\n",fname,x,y);
	}
	fclose(fp);
	return 0;
}
//////////実行結果//////////////////////
dennezu1.bmp 100 100
basutabu.bmp 210 400
Press any key to continue
///////////////////////////////////////

はい、うまくいきましたね。
もし仮に実行結果が


///////実行結果///////////////////////
dennezu1.bmp 100 100
basutabu.bmp 210 400
basutabu.bmp 210 400
Press any key to continue
///////////////////////////////////////

のようになってしまった人は、テキストファイルの一番最後に改行やスペースがないか確かめてください。
あるとしたらそれが原因で終端に達していないのです。それらを消してください。

これで画像ファイル名とその横幅と縦幅の記録を読み込むプログラムができました。
テキストファイルは別にfprintで書き込んだものじゃなくてメモ帳を使って書き込んだものでもOKです。
むしろメモ帳などのエディタで書いたものを読み込めるようにするのが目的です。
(じゃあ fprintfを使ったプログラムはちょっとした勉強用ということで覚えておいてください。)

応用問題1
改行やスペースがファイルの終端にあっても同じ画像ファイル名などを二度も表示しないようにするためにはどうしたらいいか、考えてみてください。

最初へ
INDEX        
TOPプログラムの話>C言語