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

更新:2008年11月10日


  1. 次のプログラム ある項目が読み込めないような場合を想定して、 読み込めなかった行の内容と共に, 何行目の何項目目を読み込めなかったのかを画面表示しています。 ここで,fgets関数は必ず1行読み込めると仮定しています。
    file_fgets_sscanf2.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 line=1;
    	fp=fopen("for_file5.5.1error0.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:buf=%spop2000=%d\tpop2050=%d\tch_sscanf=%d\n",
    				buf, pop2000, pop2050, ch_sscanf);
    			break;
    		}
    		printf("%d\t%s\t%s\t%d\t%d\t%d\t%d\n",
    			num, nation, region, gdp, area, pop2000, pop2050);
    		line++;
    	}
    	fclose(fp);
    	return 0;
    }
    

    この例では、「ABCDE」が「%d」の書式に合わないので 読み込みエラーが発生します。 (pop2000pop2050を表示してみると、 一つ前に読み込んだ値16928と22260のままです。自分で確認してください。) そしてsscanf関数の戻り値が7でなく5になっています。

    では「ABCDE」の代わりに次のような場合にどうなるかを確かめて、 sscanf関数の戻り値で完全にエラー処理を行うことができないことを説明してください。

    1. 「123DE」
    2. 「AB34E」
    3. 「ABC45」
    4. 「12.45」


    それぞれの場合のpop2000,pop2050,ch_sscanfの値を次の表に示します.
    設定文字列pop2000pop2050ch_sscanf
    123DE123222606
    AB34E16928222605
    ABC4516928222605
    12.4512222606

    pop2000が16928の場合やpop2050が22260の場合には35行目の項目が表示されているので 正しく読み込めていないことを示しています.

    123DE,12.45は途中まで読み込んでいることで読み込みエラーになりません. これが最後列ならばエラーの有無を検知していないことになります. sscanf関数の戻り値を確認することは,エラーを検知できるものもあるので 無駄とは云えませんが,完全ではないことに注意しておく必要があります.

  2. sscanf関数の戻り値では完全にエラー処理を行うことができないことをより深く理解するために、 次のプログラムの動作を説明してください。
    file_fgets_sscanf3.c
    #include<stdio.h>
    #include<stdlib.h>
    
    #define BUF_SIZE	1024
    
    int main(void){
    	FILE *fp;
    	int data_i;
    	double data_d;
    	char data_s[BUF_SIZE];
    	char buf[BUF_SIZE];
    	char *ch_fgets;
    	int ch_sscanf;
    
    	fp=fopen("for_report7-2.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%lf%s", 
    			&data_i, &data_d, data_s);
    		printf("ch_sscanf:%d\tdata_i:%d\tdata_d:%f\tdata_s:%s\n",
    			ch_sscanf, data_i, data_d, data_s);
    	}
    	fclose(fp);
    	return 0;
    }
    
    for_report7-2.txt
    1	2.3	efghi
    1	2	efghi
    1	2.c	efghi
    1a	2.3	efghi
    1.2	2.3	efghi
    

    実行結果は次の通りです.
    for_report7-2result.txt
    ch_sscanf:3	data_i:1	data_d:2.300000	data_s:efghi
    ch_sscanf:3	data_i:1	data_d:2.000000	data_s:efghi
    ch_sscanf:3	data_i:1	data_d:2.000000	data_s:c
    ch_sscanf:1	data_i:1	data_d:2.000000	data_s:c
    ch_sscanf:3	data_i:1	data_d:0.200000	data_s:2.3
    
    第1行目のsscanf関数による項目読み込みは正しく動作しています. 第2行目のsscanf関数による項目読み込みにおいて, 第2項目目の2は整数ですが小数2.0として読み込まれています. これは直感と合致しているので問題ありません. 第3行目のsscanf関数による項目読み込みにおいて, 第2項目目の2.cの内2.までが小数2.0として読み込まれています. 残ったcが第3項目目として読み込まれています. 結果として本来の第3項目目efghiは放置されてしまいます. これでは正しく読み込まれていませんが, ch_sscanfは3です. 第4行目のsscanf関数による項目読み込みにおいて, 第1項目目の1aの内,1までが整数として読み込まれています. 残ったaは第2項目目のdouble型として読み込めないので, ここで読み込みが中断されています. 結果として,本来の第2,3項目目2.3とefghiは放置されてしまいます. ch_sscanf1は1項目(正しくかどうかはさておき)読み込んだことを意味しています. 第5行目のsscanf関数による項目読み込みにおいて, 第1項目目1.2の内,1までが整数として読み込まれています. 残った.2が小数0.2として第2項目目に読み込まれています. よって,本来の第2項目目2.3が文字列として第3項目目に読み込まれています. 結果として本来の第3項目目efghiは放置されています. これでは正しく読み込まれていませんが, ch_sscanfは3です.
  3. sscanf関数を用いて 整数、小数、文字列を読み込もうとして誤ったデータを読み込んだ場合の動作を まとめて下さい。

    sscanf関数は, 読み込む対象を先頭から読み込める部分だけ読み込み, 読み込めなかった部分は次の読み込みに回されます. 1文字も読み込めなかった場合にはそこで読み込みが中断されます.


以下は,皆さんの幾つかの考察・感想に対するコメントです.
確実にエラー処理をするならば、最後の項目は1文字毎におかしな値ではないか調べる必 要がある。
最後の項目だけでなく全項目です.
機械語考えたのって誰ですか?
各CPU(MPU,GPU)を設計した人です.
代入されなかった値はどこに行くのだろうか。 ずっとさまよい続けているのだろうか。
file_fgets_sscanf2.cでは,代入されなかった値は(代入される値も)bufに格納されていて, ファイルからもう1行読み込まれた際に上書きされます.
中途半端に読み込まれるのは実用する際に非常に厄介だと感じた。
その通り.sscanfの使用には十分注意して下さい.
これで駄目ならば完璧なエラー処理はどのように行うのだろう。
より良い方法については strtok関数,strtol関数,strtod関数,strpbrk関数を調べてみて下さい. これらは当科目の学習範囲を越えますので説明は省きます. しかし、真に完璧なエラー処理は非常に難しいと思います。
型が違う以外のエラーではどのように反応するのか知りたい。
今一つ質問の意図が分からないので,例を示してくれると 何か解答できるかも知れません.
作れることも大事ですが、どのような結果になるかを読めるように なることも重要だと思いました。
その通り!
課題を作成しているとタイムアウトしてしまい、一度白紙に戻ってしまいました。非常に イライラしました。
emacsなどで一旦作成して完成してからフォームに貼りつけるのが良いと思います.
プログラマーの仕事に付くと先輩があまり教えてくれない、という噂を聞いた事があるが 説明する側も難しいから教えられないのではないか戸思った。
いや,そういうことでなくて,その先輩も忙しいからです. 仕事になれば技術的なことは自分で解決しなければなりません.
最近パソコンでの課題が急に増えてきてドライアイになりそうです。何かオススメの対策 はないですか?
文字を大きめに表示して,目とディスプレイの距離をとる.ディスプレイの輝度を低くする.目薬を用いる.こんなところでしょうか.
最近せぶんで働き始めました 好きなおでんはたまごです
私はきんちゃくが好きです.が,我慢してこんにゃくや白滝も食べます(;_;).

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