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

更新:2008年10月14日


  1. file_fgets1.cを改訂して, 次のように、 ASCIIのみで書かれた入力テキストファイル内に対して, 特定の文字列がある行数を画面表示して下さい.
    1. TomSawyer1.txtから,文字列"and"のある行数を数える。
    2. C.txtから、文字列"not"のある行数を数える。
    ただし,fgets関数1回で必ず1行読み込むことができると仮定して下さい. 例えば、文字列"and"を検索する際、1行に"and"が複数回現れても1行は1行です。 (ヒント:適切な文字列関数を用いて下さい。)
    report5-1.c
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    #define BUF_SIZE	256
    
    int main(void){
    	FILE *fp;
    	const char *filename1="TomSawyer1.txt";
    	const char *filename2="C.txt";
    	char buf[BUF_SIZE];
    	char *ch;
    	int cnt_and=0, cnt_not=0;
    
    	fp=fopen(filename1, "r");
    	if(fp==NULL){
    		printf("Can't Open File %s\n", filename1);
    		exit(1);
    	}
    	while(1){
    		ch=fgets(buf, BUF_SIZE, fp);
    		if(ch==NULL){
    			break;
    		}
    		if(strstr(buf, "and")!=NULL){
    		  cnt_and++;
    		}
    	}
    	fclose(fp);
    	printf("Found %d \"and\"s\n", cnt_and);
    
    	fp=fopen(filename2, "r");
    	if(fp==NULL){
    	  printf("Can't Open File %s\n", filename2);
    		exit(1);
    	}
    	while(1){
    		ch=fgets(buf, BUF_SIZE, fp);
    		if(ch==NULL){
    			break;
    		}
    		if(strstr(buf, "not")!=NULL){
    		  cnt_not++;
    		}
    	}
    	fclose(fp);
    	printf("Found %d \"not\"s\n", cnt_not);
    
    	return 0;
    }
    
  2. レポート課題(4)の解答例report4-3.cもしくは自作の解答プログラムを改訂して、 BUF_SIZE文字以上ある行を読み込もうとした場合に、 BUF_SIZE文字目以上を表示せずに、 再び次の行頭から画面表示するようにして下さい。 できれば、次のファイルのように、短いテキストファイルでの実行結果を用いてください。
    for_report5-2.txt
    1
    22
    333
    4444
    55555
    666666
    7777777
    88888888
    999999999
    
    例えばBUF_SIZEを5に設定してファイルfor_report5-2.txtを入力したときには 次のように表示するようにしてください。
    1
    22
    333
    4444
    5555
    6666
    7777
    8888
    9999
    

    report5-2.c
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    
    #define BUF_SIZE	5
    
    int main(void){
    	FILE *fp;
    	const char *filename="for_report5-2.txt";
    	char buf[BUF_SIZE];
    	char *ch;
    	int line=1;
    	char *check_whole_line;
           
    
    	fp=fopen(filename, "r");
    	if(fp==NULL){
    		printf("Can't Open File %s\n", filename);
    		exit(1);
    	}
    	while(1){
    		ch=fgets(buf, BUF_SIZE, fp);
    		if(ch==NULL){
    			break;
    		}
    		check_whole_line=strchr(buf, '\n');
    		if(check_whole_line==NULL){
    			printf("%s\n", buf);
    			while(1){
    				ch=fgets(buf, BUF_SIZE, fp);
    				check_whole_line=strchr(buf, '\n');
    				if(check_whole_line!=NULL)break;
    			}
    		}
    		else{
    			printf("%s", buf);
    		}
    	}
    	fclose(fp);
    	return 0;
    }
    
以下は皆さんの幾つかの考察・感想に対するコメントです:
strstr(buf,"and")!=NULLの!=NULLを入れなくても、実行結果が同じになりました。 入れるのと入れないのでは内容がどう違うのかがいまいち理解できませんでした。
動作は変わりません。 詳しくは、if文の動作について調べてみてください。 プログラマの中には「!=NULL」を略す人もいます。 しかし、この授業では「!=NULL」と書くことを強く勧めます。
strstr関数を使うよう指示が出されましたが、実は毎回その関数を使う理由が分かりません。
本課題では、文字列の中から文字列を探索することが主題で、 その目的に適っているのがstrstr関数だからです。
char* strstr(const char *s1, const char *s2)の関数の意味で、 「探し出した文字列の先頭文字へのポインタを返すが、 所望の文字列がなければ空ポインタを返す。」の意味がよく分からなかった。
文字列について復習してください。
if(strstr(buf,"and")!=NULL) の部分を文章にすると(説明すると)どうなるのでしょうか? 「andが含まれているなら」という意味の1行であることは、 分かるのですが...
直訳すると「関数呼出strstr(buf,"and")の戻り値が空ポインタならば」となります。 関数strstrは第一引数の文字列の中に第二引数の文字列がない場合に空ポインタを返却するので、このことを用いて意訳すると 「文字列bufの中に文字列"and"があれば」になります。
ファイルの出力はポインタ的なものを自動的に進めているのだろうと思った。 ということは、逆に戻すこともできるのだろうかと思った。
できます。例えば、fgetcに対応するのがungetcです。 より応用的にはキーワード「ランダムアクセス」で調べると良いでしょう。
今回は今までの内容を理解していたので以外に簡単だった。やはり紙に書いて一つ一つプログラムの動作を確認したので、どこに何が入っているのかなどがはっきりと分かった。これからも紙に書いて勉強したい。
比較的、他の皆さんが苦しんでいる中で、「簡単」と云えるのはやはり、 「紙に書いて一つ一つプログラムの動作を確認した」ことが大きいと思います。
strchr関数を連続で、しかも2回目はループさせて使用することなど、最初は思いつきも しませんでした。 しかし、どうしても続きの文字が表示されてしまうので、発想の転換で、「読み込み処理はさせても、 それを表示させなければいいのではないか」という結論にたどりつき、このプログラムが出来ました。
素晴らしい発送の転換ですね。
鼻炎がすごいです 最近、家のキーボードにくしゃみしたらRキーがふきとびました
笑、もとい、お大事に。

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