質問: FILE *f, *g; f= fopen(fn, "r"); g= fopen(gn,"r"); if( f==NULL){ printf("NG:%s\n",fn); exit(-1); } g= fopen(gn, "r"); if( g==NULL){ printf("NG:%s\n",gn); exit(-1); } int cf, cg; while( (cf= fgetc(f))!=EOF && (cg= fgetc(g))!=EOF ){ if( cf!=cg ) break; } if( !(cf==EOF && fgetc(g)==EOF) ) printf("%s and %s differ.\n", fn,gn); fclose(f); fclose(g); return 0; } 上記の正解出力となりません。 お助け頂きたく。
回答: ``` while( (cf= fgetc(f))!=EOF && (cg= fgetc(g))!=EOF ){ if( cf!=cg ) break; } ``` この手ね、while (cf= fgetc(f))!=EOF ・・・私もCを学び始めた頃は多用したし、周りの連中もこのパターンをよく使ってました。それなりにメリットある書き方ではあるが、でもね、これを最初から書くとワケわかんなくなりがち。発想も乏しくなる。例えば今の質問者の状態(笑)。最初からこれやっちゃいけない。初心者ならなおさら。 ### 無限ループから始めよう 繰り返しの処理を書く、とわかった時点で私流のお勧めは次のような事。 * 無限ループを書く * 無限ループの中に、一回のループでやるべきことを並べる * 並べることは、わかりやすい単純なことにする * ループ脱出には `if (条件) break;` を使う 例えば上記のループは、最初こんなふうに書いてみる。 ``` while (1) { cf = fgetc(f); if (cf == EOF) break; cg = fgetc(g); if (cg == EOF) break; id (cf != cg) break; } ``` こうしておいてから、ループの中で、何を、どのような順序にすればよいか、やり残してることはないか、じっくり考えてみる。 今回は両方から一文字ずつ読んでは比較する、それを繰り返す、ってことだ。 cf と cg の値が違うとわかった時点で不一致が確定する。一致するファイル同士なら EOF も同時に読み込めるはずだ。一方、ファイルサイズの小さい方は先に EOF が返ってきて、当然もう一方の文字と一致しないから、それも不一致と確定できる。そう考えれば「**読み込んだら EOF かどうかをまず真っ先に検査」しなくてもよい**ことに気づくだろう。即ち、ループの中の順序は変更可能。 無限ループとはいえ、いつまでもループを回すわけじゃない。言うまでもなく EOF が返る時点までだ。ではどこで EOF の判定をすればよいか、ループの先頭が良いのかどうか・・・ま、すでにループの先頭でなくても構わないと言ったようなもの(笑)で、EOF の検査はループ後半でも可能。 そこで`if (EOFが返った) break;` をループの後半に置いてみる。それがループを終了させるループ脱出条件になる(一般的にはループを終了する条件は複数ありうる)。この辺りまで来たところで、**ループ脱出条件**が無限ループの中で * ループ先頭にあるなら while (条件) { ... } に書き換えられる * ループ最後にあるなら do { ... } while (条件); に書き換えられる * ループ脱出条件がループの途中にあるなら、break のままにしておく 経験上、ループ途中で break すると良い場合は結構ある。 そもそも、このループは何をするものか。 一致するかしないかを調べるものだ。それは EOF が返る以前でも不一致が確定する場合があり、不一致が確定した時点でループを終了して構わない。 一方、一致する場合は共に EOF が返るまで、即ちファイルの最後までループが回る。つまり、こんな格好になるだろう。 ``` while (1) { .... if (cf != cg) break; // 不一致確定! // ループのどこかで EOF 判定をし、ループを脱出する .... } // 一致しても、不一致でも、ここを通る ``` ループ脱出直後の「一致しても、不一致でも、ここを通る」箇所で、どっちだったのかわからないと困るよね。当たり前だけど笑。 よくやる手はフラグ変数を使うこと。一致か不一致か、有るか無いか、表か裏か、、、といった区別をつける変数がフラグだ。ループの途中で不一致が確定する、のであるから、ループに入る前は一致するという値を持ってループを開始し、不一致が確定した時点で ``` フラグ = 一致; while (1) { .... if (cf != cg) { フラグ = 不一致; break; } ``` そして、ループから抜けた時点でフラグの値を調べれば一致・不一致がわかるという仕掛け。 ただ言っておきたいことがある。フラグ変数を覚えると「フラグさえあれば何でもできる!」みたいな気持ちになって、何でもかんでもフラグを使おうとするものだ(苦笑)。いったんフラグを使ってみるのは構わない。でもそこで**「本当にフラグは必要か?」と自分に問う**ことをお勧めする。 この場合、ループが抜けた時点で * ファイルが一致していれば、cf も cg も、どちらも値は EOF であるはず * ファイルが不一致なら cf != cg (で break した) と整理できて、この場合フラグ変数は不要・・・と、この辺りで大体ちゃんと動くものができてることだろう。 どなたか指摘した g= fopen(gn,"r"); が2回行われている他に、 ``` if( !(cf==EOF && fgetc(g)==EOF) ) printf("%s and %s differ.\n" ``` ここの fgetc(g) は非対称で不格好。まだ整理がついてない証拠。 Enjoy !