- ベストアンサー
単語の出現頻度を調べるプログラムの実行に問題がある
- ファイルからデータを読み込んで、単語の出現頻度を調べるプログラムを作成しました。
- コンパイルは成功しましたが、実行しても画面に何も表示されません。
- どこに問題があるか教えてください。
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
各行に複数単語がある場合にも対応してみました。 一部エラーチェックのコードも追加しました。 ------------------------------------------------- #include <stdio.h> #include <string.h> #define TRUE 1 #define FALSE 0 #define MAX_NWORDS 128 int isLetter(char c){ if( ('A'<=c && c<='Z') ||('a'<=c && c<='z') ) { return TRUE; }else{ return FALSE; } } main() { struct data { char word[128]; int freq; } word_data[MAX_NWORDS]; char term[128]; int nw=0; int i,j,k; char buff[128]; FILE *fp_in=fopen("data.txt","r"); while(fgets(buff,128,fp_in)!=NULL){ k=0; while(buff[k]!='\n'){ while(! isLetter(buff[k]) && buff[k]!='\n') k++; if(buff[k]=='\n')break; i=0; while(isLetter(buff[k])) term[i++]=buff[k++]; term[i]='\0'; for(j=0;j<nw;j++) if(strcmp(term,word_data[j].word)==0) break; if(j==nw) { strcpy(word_data[j].word,term); word_data[j].freq=1; nw++; if(nw==MAX_NWORDS){ printf("more than %d words found.\n", MAX_NWORDS); exit(1); } } else { word_data[j].freq++; } } } for(j=0;j<nw;j++) printf("%s %d\n",word_data[j].word,word_data[j].freq); return 0; }
その他の回答 (6)
- wolv
- ベストアンサー率37% (376/1001)
修正版。動作確認ずみ。 strcmpを使っている部分は2箇所ありますが、 たぶん、2箇所めのstrcmpのみをこのstrcmp_dictorderに変更するのがよいと思います。 (大文字、小文字の違いによって意味が違う単語もありますので。) int strcmp_dictorder(char*a,char*b){ int i; char ca,cb; for(i=0;i<=strlen(a);i++){ ca=a[i];if(ca>='a')ca-=32 ; cb=b[i];if(cb>='a')cb-=32 ; if(ca!=cb) return ca-cb; } for(i=0;i<=strlen(a);i++){ if (a[i]!=b[i]) return a[i]-b[i]; } return 0; }
お礼
何度も丁寧にありがとうございます。 先ほど、実行してみましたところ、小文字の後に大文字が続く個所が数箇所あったのですが、 最初のforループに if(ca==cb-32) return 0; if(ca-32==cb) return 1; の2文を追加してみましたところ大文字→小文字の順番に上手く並びました。 文字列の比較では、strcmp関数を使うといったことしか 思い浮かばなかったので悩んでいたのですが、自分で関数を作ってしまえばいいのですね。また、教えて頂いた大文字と小文字を無視した大小関係の比較の書き方はなるほどと思いました。 お忙しいところ、丁寧に回答していただきまして本当にありがとうございました。これからも何か機会がありましたらよろしくお願いします。
- wolv
- ベストアンサー率37% (376/1001)
以下のような関数を定義して、strcmpの代わりにつかってみてください。返り値の符号は逆のほうがいいかもしれません。結果を見て適当に直してください。 動作確認はしていません。 下の関数のa[i]でエラーがでるようなら、 a[i]を*(a+i)に変えてみてください。 int strcmp_dictorder(char*a,char*b){ int i; char ca,cb; for(i=0;i<=strlen(a);;i++){ ca=a[i];if(ca>='a')ca-=32 cb=b[i];if(cb>='a')cb-=32 if(ca<cb)return -1; else if (ca>cb)return 1; else if (a[i]<b[i])return -1; else if (a[i]>b[i])return 1; else ;/*compare next letter*/ } return 0; }
- wolv
- ベストアンサー率37% (376/1001)
No3までを踏まえて動作するように書き換えてみました。制御構造も一部かえました。回答No2に示したようなdata.txtに対して動作確認ずみです。 ------------------------------------------------- #include <stdio.h> #include <string.h> main() { struct data { char word[128]; int freq; } word_data[128]; char term[128]; int i; int nw=0; int j; char buff[128]; FILE *fp_in=fopen("data.txt","r"); while(fgets(buff,128,fp_in)!=NULL){ int k; i=0; for(k=0;k<=128;k++){ if( ('A'<=buff[k] && buff[k]<='Z')||('a'<=buff[k] && buff[k]<='z') ) { term[i++]=buff[k]; } else { break; } } if(i>0){ term[i]='\0'; for(j=0;j<nw;j++) if(strcmp(term,word_data[j].word)==0) break; if(j==nw) { strcpy(word_data[j].word,term); word_data[j].freq=1; nw++; } else { word_data[j].freq++; } } } for(j=0;j<nw;j++) printf("%s %d\n",word_data[j].word,word_data[j].freq); return 0; } ------------------------------------------------- 注意:上記ソースコードは、行頭に全角スペースが入っているため、そのままコピーペーストしてもコンパイルできないことがあります。
- wolv
- ベストアンサー率37% (376/1001)
・char buffの宣言が2箇所にあります。そのためファイルから読み込んだ文字列は、while文の内部で使われていません。 ・fgetsの書式が違います。 fgets(buff,128,fp_in,128,buff)のはずです。 ・fgetsの返すファイル終端をあらわす値はEOFではなくNULLです。 ・結果表示部で、word_dat"e"[j]を表示しようとしています。 ・結果は、各単語の情報を表示するように改行も出力したほうがよさそうです。 ・char words[128]は使われていません。
- wolv
- ベストアンサー率37% (376/1001)
・各行のはじめの単語しかチェックしない仕様のようですが、それでいいのでしょか? ・行のはじめの1文字がA-Za-zのいずれかでない場合、term[0]の値が不定になります。この状態でstrcmpを実行すると問題がある気がします。 (上の2点は、各行が1単語からなり、その単語は行の1カラム目から始まる、という場合は問題ありませんが...) ・すでに登録した単語と同じ単語を見つけたばあい、iの値を0にリセットせずに次の行を読み込んでいます。次の行の単語は、term中の前回の単語の後ろに連結されます。 ( たとえば、データファイルが and and or だった場合、orを読み込んだ時点でのtermは、"andor"になります。(他の部分の動作に問題がなかったとしても) )
- wolv
- ベストアンサー率37% (376/1001)
for(j=0;j<nw;j++){ のループの中に、 if(j==nw) という条件式があります。"thenの部分"は実行されません。
補足
丁寧にありがとうございます。 もう1点質問させていただきたいのですが、最終結果を出力するときに辞書式順序(A,a,B,b,・・,Z,z)にソートする場合はどうすればいいのでしょうか?昨晩自力でやってみたのですが、どうもうまくいきません。アスキーコードの配列を用いる気がするのですが、アスキーコードですと、'A'~'Z'の後に、'a'~'z'が続きますよね。これをそのまま用いると、大文字が先にまとめて表示されてしまい、小文字が後にまとめて表示されてしまいますよね。 また、'A'='a'-32ですので、一旦、全ての小文字を大文字に変換して、大小比較をし、また元に戻すといった手段も考えてみたのですが、私にはソースコードが書けませんでした。 宜しければ、ご回答お願いできますでしょうか? 以下に私が書いてみたソースコードを挙げておきます。 #include <stdio.h> #include <string.h> #include <stdlib.h> #define TRUE 1 #define FALSE 0 #define MAX_NWORDS 128 int isLetter(char c){ if(('A'<=c && c<='Z')||('a'<=c && c<='z')){ return TRUE; }else{ return FALSE; } } main(){ struct data{ char word[128]; int freq; }; struct data word_data[MAX_NWORDS]; struct data word_data_cp[MAX_NWORDS]; char term[128]; int nw=0; int i,j,k,p,x; char m; char buff[128]; FILE *fp_in=fopen("data.txt","r"); while(fgets(buff,128,fp_in)!=NULL){ k=0; while(buff[k]!='\n'){ while(!isLetter(buff[k]) && buff[k]!='\n') k++; if(buff[k]=='\n')break; i=0; while(isLetter(buff[k])) term[i++]=buff[k++]; term[i]='\0'; for(j=0;j<nw;j++) if(strcmp(term,word_data[j].word)==0) break; if(j==nw){ strcpy(word_data[j].word,term); word_data[j].freq=1; nw++; if(nw==MAX_NWORDS){ printf(" more than %d words found.\n",MAX_NWORDS); exit(1); } }else{ word_data[j].freq++; }}} for(p=0;p<nw;p++){ for(j=0;j<nw-p-1;j++){ x=strcmp(word_data[j].word,word_data[j+1].word); if(x>0){ word_data_cp[j]=word_data[j]; word_data[j]=word_data[j+1]; word_data[j+1]=word_data_cp[j];} }} for(j=0;j<nw;j++) {printf("%s %d\n",word_data[j].word,word_data[j].freq);} printf("Total %d Words\n",nw); return 0; }