情報処理4レポート課題(6)の解答例

更新:2008年10月27日


  1. file_fgets_sscanf.cを改訂して,for_file5.5.1.txtに対して、
    1. 第5列目の値が1000以上20000未満の情報だけを画面表示するようにプログラムを改訂して下さい.実行結果は次の通り:
      report6-1result.txt
      1	Algeria	Africa	1520	2382	31599	58991
      2	Argentina	Latin-America	8380	2767	37032	54522
      3	Australia	Oceanea	20090	7713	18838	25286
      7	Bolivia	Latin-America	830	1099	8329	16966
      8	Brazil	Latin-America	4400	8512	169202	243259
      12	Canada	North-Americal	19020	9976	30679	36352
      14	China	Asia	750	9561	1276301	1516664
      15	Colombia	Latin-America	2140	1139	38905	62284
      19	Egypt	Africa	1080	1001	68119	115480
      20	Ethiopia	Africa	100	1097	66175	212732
      28	India	Asia	380	3288	1006770	1532674
      29	Indonesia	Asia	1080	1905	212565	318264
      30	Iran	Asia	2300	1648	76429	170269
      35	Kazakstan	Asia	1350	2671	16928	22260
      42	Mali	Africa	240	1240	12559	36817
      43	Mexico	Latin-America	3670	1958	98881	154120
      49	Niger	Africa	200	1267	10805	34576
      53	Peru	Latin-America	2420	1285	25562	42292
      58	Russia	Europe	2410	17075	146196	114318
      60	South-Africa	Africa	3520	1221	46257	91466
      61	Saudi-Arabia	Asia	6800	2150	21661	59812
      73	U.S.A.	North-America	28020	9809	277825	347543
      

      report6-1.c
      #include<stdio.h>
      #include<stdlib.h>
      
      #define BUF_SIZE	1024
      
      int main(void){
      	FILE *fp;
      	int num, area, gdp, pop2000, pop2050;
      	char *ch_fgets;
      	int ch_sscanf;
      	char nation[256], region[256];
      	char buf[BUF_SIZE];
      	fp=fopen("for_file5.5.1.txt", "r");
      	if(fp==NULL){
      		printf("Can't Open File\n");
      		exit(1);
      	}
      	while(1){
      	
      		ch_fgets=fgets(buf, BUF_SIZE, fp);
      		if(ch_fgets==NULL){
      			break;
      		}
      		ch_sscanf=sscanf(buf, "%d%s%s%d%d%d%d", 
      			&num, nation, region, &gdp,
      			&area, &pop2000, &pop2050);
      		if(ch_sscanf!=7){
      			printf("Read Error\n");
      			break;
      		}
      		if(area>=1000 && area<20000){
      			printf("%d\t%s\t%s\t%d\t%d\t%d\t%d\n",
      				num, nation, region, gdp, area, pop2000, pop2050);
      		}
      	}
      	fclose(fp);
      	return 0;
      }
      
    2. 第3列目の値がAsiaである情報だけを画面表示するようにプログラムを改訂して下さい.実行結果は次の通り:
      report6-2result.txt
      5	Bangladesh	Asia	260	144	128310	218188
      14	China	Asia	750	9561	1276301	1516664
      28	India	Asia	380	3288	1006770	1532674
      29	Indonesia	Asia	1080	1905	212565	318264
      30	Iran	Asia	2300	1648	76429	170269
      32	Israel	Asia	15870	21	6077	9144
      34	Japan	Asia	40940	378	126428	109546
      35	Kazakstan	Asia	1350	2671	16928	22260
      37	Korea(South)	Asia	10610	99	46883	52146
      38	Kuwait	Asia	18720	18	1966	3406
      41	Malaysia	Asia	4370	330	22299	38089
      46	Nepal	Asia	210	141	24347	53621
      52	Pakistan	Asia	480	796	156007	357353
      54	Philippines	Asia	1160	300	75037	130511
      59	Singapore	Asia	30550	1	2848	3355
      61	Saudi-Arabia	Asia	6800	2150	21661	59812
      63	Sri-Lanka	Asia	740	66	18821	26995
      67	Thailand	Asia	2960	513	60495	72969
      69	Turly	Asia	2830	779	65732	97911
      75	Vietnam	Asia	290	332	80549	129763
      76	Yemen	Asia	380	528	18118	61129
      
      strstrでは,"South-Asia"にもヒットしてしまいます. (今回扱っているファイルにはないけれど)
      report6-2.c
      #include<stdio.h>
      #include<stdlib.h>
      #include<string.h>
      
      #define BUF_SIZE	1024
      
      int main(void){
      	FILE *fp;
      	int num, area, gdp, pop2000, pop2050;
      	char *ch_fgets;
      	int ch_sscanf;
      	char nation[256], region[256];
      	char buf[BUF_SIZE];
      	fp=fopen("for_file5.5.1.txt", "r");
      	if(fp==NULL){
      		printf("Can't Open File\n");
      		exit(1);
      	}
      	while(1){
      	
      		ch_fgets=fgets(buf, BUF_SIZE, fp);
      		if(ch_fgets==NULL){
      			break;
      		}
      		ch_sscanf=sscanf(buf, "%d%s%s%d%d%d%d", 
      			&num, nation, region, &gdp,
      			&area, &pop2000, &pop2050);
      		if(ch_sscanf!=7){
      			printf("Read Error\n");
      			break;
      		}
      		if(strcmp(region, "Asia")==0){
      			printf("%d\t%s\t%s\t%d\t%d\t%d\t%d\n",
      				num, nation, region, gdp, area, pop2000, pop2050);
      		}
      	}
      	fclose(fp);
      	return 0;
      }
      
    3. 第4列目の平均値を画面表示するようにプログラムを改訂して下さい.
      report6-3.c
      #include<stdio.h>
      #include<stdlib.h>
      
      #define BUF_SIZE	1024
      
      int main(void){
      	FILE *fp;
      	int num, area, gdp, pop2000, pop2050;
      	char *ch_fgets;
      	int ch_sscanf;
      	char nation[256], region[256];
      	char buf[BUF_SIZE];
      	int sum=0;
      	int num_nation=0;
      
      	fp=fopen("for_file5.5.1.txt", "r");
      	if(fp==NULL){
      		printf("Can't Open File\n");
      		exit(1);
      	}
      	while(1){
      	
      		ch_fgets=fgets(buf, BUF_SIZE, fp);
      		if(ch_fgets==NULL){
      			break;
      		}
      		ch_sscanf=sscanf(buf, "%d%s%s%d%d%d%d", 
      			&num, nation, region, &gdp,
      			&area, &pop2000, &pop2050);
      		if(ch_sscanf!=7){
      			printf("Read Error\n");
      			break;
      		}
      		sum+=gdp;
      		num_nation++;
      	}
      	printf("Average GDP:%f\n", ((double)sum)/((double)num_nation));
      	
      	fclose(fp);
      	return 0;
      }
      
    4. 第5列目が最大値を取る国名を画面表示するようにプログラムを改訂して下さい.
      report6-4.c
      #include<stdio.h>
      #include<stdlib.h>
      #include<string.h>
      
      #define BUF_SIZE	1024
      
      int main(void){
      	FILE *fp;
      	int num, area, gdp, pop2000, pop2050;
      	char *ch_fgets;
      	int ch_sscanf;
      	char nation[256], region[256];
      	char buf[BUF_SIZE];
      	int max_area=0;
      	char max_area_nation[256];
      
      	fp=fopen("for_file5.5.1.txt", "r");
      	if(fp==NULL){
      		printf("Can't Open File\n");
      		exit(1);
      	}
      	while(1){
      	
      		ch_fgets=fgets(buf, BUF_SIZE, fp);
      		if(ch_fgets==NULL){
      			break;
      		}
      		ch_sscanf=sscanf(buf, "%d%s%s%d%d%d%d", 
      			&num, nation, region, &gdp,
      			&area, &pop2000, &pop2050);
      		if(ch_sscanf!=7){
      			printf("Read Error\n");
      			break;
      		}
      		if(max_area<area){
      			max_area=area;
      			strcpy(max_area_nation, nation);
      		}
      	}
      	printf("Nation with maximal area:%s\n", max_area_nation);
      	
      	fclose(fp);
      	return 0;
      }
      
以下は皆さんの幾つかの考察・感想に対するコメントです.
最近、プログラムの改編しかしていないので一から書くのに苦労する気がします。
その危機感を持てるならば,授業とは別途に是非,一から書く訓練をお勧めします.
空白を使った時の読み込み方について謎が残った。
対象ファイルでは区切り文字として空白類文字を用いているので, レコードに空白を入れることはできません. レコードに空白を入れるためには区切り文字を別の文字にする必要があり, また,sscanf関数の%変換において区切り文字を指定する必要があります. が,この科目の学習範囲を越えるので興味があれば自分で調べてみて下さい.
「 4で少しつまった。 char *con のように宣言したものに、直接nationを入れようとするとバグがおこってしま った。」 「問題4のセグメントエラーの意味がよくわかりません。」
conは,あくまでもアドレスを保持する変数であって, 単独では文字列を格納できません. nationと同じ要素数を持つchar型配列を用意して, それにコピーしなければなりません.
for文使えば文字列関数使わなくても大丈夫ですよね?
確かにそうですが,文字列関数を使った方が楽に書けますよね.
/t タブっぽい
\tはタブです.スラッシュ/とバックスラッシュ\の違いに気をつけましょう.
!=NULLがいらないということの考察 if(a)では、「aが0なら偽、それ以外なら真」であり、 strstr(buf,"and")で見付かったら値を持つので!=NULLをつける必要がないのかなと思っ た。
そうです.
このようなプログラムを組んでいると、国々を調べたい値ごとにソートをかけたくなった 。 ただ、ソートと言えば配列のイメージがあるが、この場合はうまいことできそうなのが思 いつかなかった。 やろうとしてもかなり大変な作業になりそうだと感じた。 国ごとの固有番号をふって、すべての値の配列を作って、順位ごとに表示して・・・。 あぁ、値の比較後に、それを他の配列に反映させるのをどうすれば・・・。
ソートについて不可能ではありませんが,簡単ではありません. まずは,全レコードを配列に格納することになり, そのためには,配列の要素数,つまり,行数を調べなければなりません. 興味があれば挑戦してみて下さい.
問題3はすべての値の和をとったあと単純に78で割らずに、足した個数分カウンタをイン クリメントし最後に それで割るようにしたので、要素の数が変わっても対応できるように 工夫できた。
良いプログラミング作法です.
最初、strcpyを使わずに直接MAX_natに国名を入れようとしたらジンバブエしか表示され なかった。しかしとりあえず配列を使ってstrcpyを使うとできるようになった、、、何故 配列にしないといけないのか分からなかった。 一回コピーしないとうまく表示されないのもよく分からない、、、
MAX_nat=nation;は配列nationの先頭アドレスをMAX_natに代入することになります. if(max<=area)の判定にヒットして MAX_nat=nation;が実行された後に, sscanf関数呼び出しによって,nationの要素は書き換わります. 最終的に,MAX_natには,最後のsscanf関数呼び出しによって"Zimbabwe"が格納されたnationの先頭アドレスを保持していることになってしまいます.
文字操作関数とかって、やっぱり全部覚えた方 がいいんでしょうか?一般的なプログラムにも結構使うものですよね?
使っているうちに覚えるものです.丸暗記は禁物です. 実際,私もうろ覚えで,確実なプログラムを作成する際には, 文字列関数の表を確認します.
文字列大っ嫌いです。
その内,そうでもなくなりますよ.好きになるかは分かりませんが.
けつが痛い。 もっと柔らかい椅子が欲しい...
要望が叶うのは難しいと思うので,自衛手段として座蒲団を持参することを勧めます.
最初strncpyの部分を
j=0
while(1){
if(nation == '\0')break;
max_area_nation[j]=nation[j];
j++;
}
としたのだがうまくいかなかったのでstrncpyを用いた 前にこういう方法でコピーしたような気もするし、この方法はダメみたいな気もします これでうまく回らないのはどうしてなんでしょうか?
nation[j]'\0'を比較しなければなりません.
4番でもそうだがforを使って最大値を出そうとしたがareaをmaxと比べるのがうまくいか ず結局友達に聞いてwhileのなかに入れてやってみたらできた。 この問題でforを使って解くことはできますか?
while(1){
	...
	if(ch_fgets==NULL){
		break;
	}
	...
}
の代わりに,
for(;;){
	...
	if(ch_fgets==NULL){
		break;
	}
	...
}
とすることもできます.
if(n==0)
          {
            max=area;
          }
自分のプログラムでは上の文が抜けていただけだったのですが、なぜこの文が必要なのか 理解できませんでした。
maxをあり得る最小値(この場合では0)に初期化していないのが原因です.
forだとgdpがgdp[i]みたいな形じゃないと解けないのかなと思った。
そんなことはありません. for文もwhile文も同じように,ループを作るための手段であって, while文で書けることはfor文でも書けますし,逆も成立します.
max_nation=nationではなぜ出来ないのだろうか、どうして配列を使うのか疑問だった。
過去のレポート課題を復習して下さい.
最近、Rキーがなくなったので比較的使わないF12を変わりにいれておきました
なるほど.まあ,キーの印字なんて最終的は不要ですからね.
僕のRキーは健在です。
よかったです.Rは結構使いますからね.

何かありましたら、 まで。