テキストファイルの比較

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ちょっぱー

テキストファイルの比較

#1

投稿記事 by ちょっぱー » 17年前

はじめまして!
C言語を始めて一か月なのですが、今、2つのテキストファイルを読み込んで、内容を比較し、一致しているようならsuccess, 間違っているなら間違っている箇所を出力するプログラムを作成しています。
ソースコードは以下の通りです。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

FILE *f_in, *f_out ;

int main(void){
char str_in[10];
char str_out[10];
int i, cmp ;
   
f_in = fopen ("file1.txt", "r");
f_out = fopen ("file2.txt", "r");

    if ( f_in == NULL || f_out == NULL){
    printf( "Can't open file.\n" );
    return 0 ;
    }

    while ( (fgets( str_in, 10, f_in )) != NULL && (fgets( str_out, 10, f_out )) != NULL)) {
         str_in[strlen(str_in) - 1] = 0 ;
         str_out[strlen(str_out) - 1] = 0 ;
            for (i=0; i < strlen(str_in) && i < strlen(str_out) ; i++) {
                cmp = strcmp(str_in, str_out) ;
                   if (cmp == 0)  {
                       printf ( "success.\n" ) ;
                       return 0 ;
                   }
                   else {
                       printf ( "fail.\n" ) ;
                       printf ( "%s and %s are mismatched.\n", str_in, str_out ) ; 
                       return 0 ;
                   }
           fclose( f_in );
           fclose( f_out );
    }
return 0 ;
}
仮にfile1.txtが


34
であり、file2.txtが



であったとすると、このプログラムを実行すると、テキストファイルが違うにも関わらず
success.
と表示されてしまいます。
(ほんとは
fail.
34 and 6 are mismatched.
と出力されてほしいのです。)
恐らく1行目しか評価されていないことが原因だと思うのですが、すべての行でstrcmpを行うにはどうしたらよいかわからず途方に暮れております。なにかよい知恵やソースコードを見て気づいたこと等ありましたら、ご教授ください。
よろしくお願いいたします。

御津凪

Re:テキストファイルの比較

#2

投稿記事 by 御津凪 » 17年前

(不一致=エラーと解釈してください)
エラーが発生したかどうかのフラグを用意し、

strcmp での条件のところで、
・一致していたら次の行へ
・違っていたらエラー内容を出力し、エラーフラグを立てる
とします。

そして、全ての行をチェックし終えたときに、
エラーフラグをチェックし、フラグが立っていなければ
"success."を出力すればいいと思います。

エラーフラグをエラー回数に直せば、エラーが発生した数が分かりますし、
行番号の変数を用意(1行目から始まるので1で初期化)し、毎ループ +1 していけば、
エラーが発生した行番号を特定できますよ。

それともう一つ。
このコードだと、一致している2つのテキストファイルの片方に行を追加した場合、
エラーが出ず、"success."が出力されてしまいます。

ファイルが双方とも終端に達しているかのチェックも入れたほうがいいと思います。

Mist

Re:テキストファイルの比較

#3

投稿記事 by Mist » 17年前

fgetsの理解が間違っているとおもいます。
fgetsは改行(\n)に出会うと読むのをやめるので
> fgets( str_in, 10, f_in )
では一行ずつしか読めません。
なので、中のforは無意味です。

あと、whileやforを使っているけどforの中のif~elseでどちらにいっても必ずreturnされているので、繰り返されることが無いです。
fcloseも実行されないので開きっぱなしです。

以下のようにすればよいのではないでしょうか。
ミスマッチフラグ=OFF
while(両方のファイルから1行ずつ読む) {
  文字列を比較する
  if (不一致) {
   ミスマッチと出力する
   ミスマッチフラグ=ON
   ループを抜ける(break)
  }
}
if (ミスマッチフラグ=OFF) {
 サクセスと出力
}

ファイルクローズ


[/pre]

box

Re:テキストファイルの比較

#4

投稿記事 by box » 17年前

> ソースコードは以下の通りです。

インデントにポリシーが感じられなくて見づらいです。
それから、提示のコードをそのままコンパイルするとエラーが出ます。
Borland C++ Compilerでコンパイルしたときのエラーメッセージを提示します。


エラー E2188 D:\My Programs\C\temp1\temp1.c 20: 式の構文エラー(関数 main )
エラー E2134 D:\My Programs\C\temp1\temp1.c 38: 複合文に } がない(関数 main )
警告 W8070 D:\My Programs\C\temp1\temp1.c 38: 関数は値を返すべき(関数 main )
警告 W8080 D:\My Programs\C\temp1\temp1.c 38: 宣言された 'cmp' は使われていない(関数 main )
*** コンパイル中に 2 個のエラーが発生しました ***


というわけで、実行できたときのソースコードをそのまま見せてください。

ちょっぱー

Re:テキストファイルの比較

#5

投稿記事 by ちょっぱー » 17年前

>御津凪様、Mist様

ありがとうございます.
おっしゃられた方法で少し試してみます.

>box様

申し訳ありません.コンパイル時のファイルは以下の通りです.
(ヘッダーは省略してあります.)
FILE *f_in, *f_out;

int main(void) {
	char str_in[10];
	char str_out[10];
	int i ;/*for*/
	int cmp;
	
	f_in = fopen ( "abc.txt","r" ) ;
	f_out = fopen ( "123.txt", "r" );

	if (f_in == 0 || f_out == 0 ) {
		printf( "Unable to open file.\n" ) ;
        return 0;
	}
   
	if (f_in == 0 || f_out == 0 ) {
		printf( "Unable to open file.\n" ) ;
        return 0;
	}

    while ((fgets( str_in, 10, f_in )) != NULL && (fgets( str_out, 10, f_out )) != NULL) {
        str_in[strlen(str_in) - 1] = 0 ;
        str_out[strlen(str_out) - 1] = 0 ;

        for (i=0; i < strlen(str_in) && i < strlen(str_out) ; i++) {
	    cmp = strcmp(str_in, str_out) ;
			if (cmp == 0)  {
			printf ( "success.\n" ) ;
            return 0 ;
			}
			else {
            printf ( "fail.\n" ) ;
            printf ( "%s and %s are mismatched.\n", str_in, str_out ) ; 
            return 0 ;
			}
		}
	}
	fclose( f_in );
    fclose( f_out );
    return 0 ;
}

non

Re:テキストファイルの比較

#6

投稿記事 by non » 17年前

str_in[strlen(str_in) - 1] = 0 ;
str_out[strlen(str_out) - 1] = 0 ;

fgetsは自動的に'\0'を最後につけてくれますが、そのためのものでしょうか?
でも、strlen(str_in) - 1 なのはなぜ?

non

Re:テキストファイルの比較

#7

投稿記事 by non » 17年前

strlen(str_in) - 1 なのはなぜ?
fgets って、改行文字が最後に付くのですね。わかりました。
改行文字まで、比較してもかまわないから、やっぱり、この2行はいらないのでは?

通りすがりの者

Re:テキストファイルの比較

#8

投稿記事 by 通りすがりの者 » 17年前

 

ちょっぱー

Re:テキストファイルの比較

#9

投稿記事 by ちょっぱー » 17年前

数々のアドバイスまことにありがとうございます。
皆様のご意見を参考にしまして、以下のソースコードに変更しました。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN (10)

FILE *f_in, *f_out ;

int main(void) {
	char str_in[LEN], str_out[LEN] , *p, *q ;
	int i, cmp, num , err_flag;
    num = 1 ;
	
	f_in = fopen ( "abc.txt", "r" ) ;
	f_out = fopen ( "123.txt", "r" );
	
	if (f_in == NULL || f_out == NULL ) {
		printf( "Unable to open file.\n" ) ;
        exit(2) ;
	}

	err_flag = 0 ;
    
	while ( (p = fgets(str_in, LEN, f_in)) != NULL && (q = fgets(str_out, LEN, f_out)) != NULL) {
          ++num ;
	    str_in[strlen(str_in) - 1] = 0 ;
          str_out[strlen(str_out) - 1] = 0 ;
          cmp = strcmp(str_in, str_out) ; 
		
		if ( cmp != 0) {
		printf ( "failed:%s  and %s in line%d are mismatched.\n", str_in , str_out , num ) ;
		err_flag = 1;
		}	
	}
	
	if ( err_flag == 0) {
		if ( p == NULL && q == NULL ) {
		printf ("success.\n") ;
		}
		else{
		printf ( "number of lines is different.\n" ) ;
		}
	}
fclose( f_in );
fclose( f_out );
return 0 ;
}
abc.txtを
FB5502
555500
555500
555500
55D500
とし、123.txtを
FB5502
555500
555500
555500
55D500
と同じファイルの状態にしてコンパイルしたところ、

failed:55D500 and 55D50 in line6 are mismatched.

と出力されてしまいました...
現状の私の知識ではソースコードによるものなのか、他の要因によるものなのかわからず、苦戦しております。
原因のわかる方いらっしゃいましたら、ヒントなどいただけないでしょうか?
よろしくお願いします。

御津凪

Re:テキストファイルの比較

#10

投稿記事 by 御津凪 » 17年前

片方のファイルの終端に改行がないことが原因ではないでしょうか?
改行で終わらないファイルの場合、最後に改行がつかないため、
改行を削除する処理の部分で最後の文字を消してしまいます。

削除する文字が本当に改行文字なのかを確認してから削除するといいと思います。

Mist

Re:テキストファイルの比較

#11

投稿記事 by Mist » 17年前

提示してもらったソースコードを私の環境(BCC)でコンパイルして実行してみましたが、
number of lines is different.
と表示されました。
最後の行で失敗しているようなので、予想になりますが片方は

55D500(改行)
[EOF]

で、もう一方は

55D500[EOF]

となっていませんか?
この場合

str_in[strlen(str_in) - 1] = 0 ;
str_out[strlen(str_out) - 1] = 0 ;

この部分が影響して不一致になります。

あと、今の現象には関係ないですがnumを1で初期化するのは間違いです。
whileに入った直後に++numとしているので、1行目が不一致でも2と表示されます。

box

Re:テキストファイルの比較

#12

投稿記事 by box » 17年前

仮に、2つの入力ファイルの内容が最後の改行も含めて全く同一であるとします。
このとき、

> 	while ( (p = fgets(str_in, LEN, f_in)) != NULL && (q = fgets(str_out, LEN, f_out)) != NULL) {

ここを最後に実行すると、p が NULL になった時点で
(q = fgets(str_out, LEN, f_out)) != NULL
を実行・評価しません。&& 条件とは、そういうものです。
つまり、p は NULL ですが、q は NULL ではない状態です。
したがって、

> 	if ( err_flag == 0) {
> 		if ( p == NULL && q == NULL ) {

この条件を満たしませんので、

> 		printf ( "number of lines is different.\n" ) ;

こちらを出力します。

いいところまで進んでいますが、あと一工夫必要です。

ちょっぱー

Re:テキストファイルの比較

#13

投稿記事 by ちょっぱー » 17年前

皆様、ご意見ありがとうございます.
ご指摘の通り、EOFの場所の違いだったようです.
また、&&の順序については恥ずかしながらはじめて知りました.
勉強になります.
しかし、そうしますとこれでは常に行数が違うと表示されてしまいますね…

勉強不足で大変申し訳ないのですが、p=NULLとなったときの処理についてはfeof()を用いて、abc.txtと123.txtの読み込みが完了したフラグを認識してから
if ( err_flag == 0) {
		if ( p == NULL && q == NULL ) {
		printf ("success.\n") ;
		}
		else{
		printf ( "number of lines is different.\n" ) ;
		}
	}
の部分を評価すればよいと今考えているのですが、どうもうまくいきません.
この考え方は間違っていますでしょうか?
もし間違っているようなら、何か解決策のヒントをいただけると幸いです.
よろしくお願いいたします.

御津凪

Re:テキストファイルの比較

#14

投稿記事 by 御津凪 » 17年前

box さんのおっしゃった、 && の問題があるので、
> while ( (p = fgets(str_in, LEN, f_in)) != NULL && (q = fgets(str_out, LEN, f_out)) != NULL){
上記の部分の条件文を、
p = fgets(str_in, LEN, f_in);
q = fgets(str_out, LEN, f_out);
while ( p != NULL && q != NULL ){
として、両方のファイルを必ず読み込むようにしていますか?
両方とも読み込んでから評価しないと、 && の性質で最初の方(str_in)しか読み出しません。

ちょっぱー

Re:テキストファイルの比較

#15

投稿記事 by ちょっぱー » 17年前

>御津凪様

上記のソースファイルにおいて、while文周辺のみを
p = fgets(str_in, LEN, f_in) ;
      q = fgets(str_out, LEN, f_out) ;
	str_in[strlen(str_in) - 1] = 0 ;
	str_out[strlen(str_out) - 1] = 0 ;
	
	err_flag = 0 ;
    
	while ( p != NULL && q != NULL) {
   	++num ;
	cmp = strcmp(str_in, str_out) ; 
		if ( cmp != 0) {
		printf ( "failed : %s and %s in line%d are mismatched.\n", str_in , str_out , num ) ;
		err_flag = 1;
		}
	break ;
	}
のように書き換えて、コンパイルしたのですが、結果が何も変わらなかったため、方針を変えた次第です.
このソースコードは正しいでしょうか…?

御津凪

Re:テキストファイルの比較

#16

投稿記事 by 御津凪 » 17年前

すいません、コード間違えていました。
p = fgets(str_in, LEN, f_in);
q = fgets(str_out, LEN, f_out);
while ( p != NULL && q != NULL ){
このままだと、読み込み処理が一回しかされません。
while((p = fgets(str_in, LEN, f_in)) != (q = fgets(str_out, LEN, f_out)))
とすればいいです。
(str_in と str_out が同じ配列でない限り、 false になるのは両方とも NULL の時だけ)
これで両方 NULL の時に "success." と出せるはずです。

ちょっぱー

Re:テキストファイルの比較

#17

投稿記事 by ちょっぱー » 17年前

おっしゃる通りに変更したら、うまくいきました!

皆様からのご助言・ご意見は勉強になるものばかりで、今後の励みにもなりました。
恐らく今後もいくつかの壁にあたると思うので、その際はまた勉強させてください。

どうもありがとうございましたm(_ _)m

閉鎖

“C言語何でも質問掲示板” へ戻る