• 締切済み

c言語で文書検索プログラムについて質問です。

いつもお世話になっています。 今回、大学の課題でc言語を用いて文書検索のプログラムを作成しています。 クエリ(検索単語)を2つ入力して、クエリ2つを同じ行に含む1文が存在した場合そのテキストファイルのファイル名を出力するプログラムを作成しています。 検索する文書は、ソースファイルと同じディレクトリにあるcorpusディレクトリ内のテキストファイルについて行います。 概ね、自力で作成はできているよう思うのですが、どうしても出力がうまくいきません。 出力条件、もしくはstrcmpのあたりに原因があるような気がするのですが・・・。 以降に私の作成したソースファイルを添付しておきますので、 原因のわかる方ぜひお願いします。 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<ctype.h> int main(int argc, char* argv[]){ FILE* fp; char buffer[1024]; char kueri1[256]; char kueri2[256]; int flag_kueri = 0; //kueri1,kueri2がどちらもあれば1に int flag_kueri1 = 0; //kueri1があれば1に int flag_kueri2 = 0; //kueri2があれば1に int i; cahr* s; char* delimiter = " .,"; /*コマンドラインが正しく入力されているかの確認*/ if(argc < 4){ printf("error1\n"); exit(1); } /*入力されたクエリ1をkueri1に格納*/ strcpy(kueri1,argv[1]); /*入力されたクエリ2をkueri2に格納*/ strcpy(kueri2,argv[2]); /*kueri1の英小文字を英大文字に変換する処理を\0まで繰り返す*/ s = kueri1; while(*s != '\0'){ *s = toupper(*s); s++; } /*kueri2の英小文字を英大文字に変換する処理を\0まで繰り返す*/ s = kueri2; while(*s != '\0'){ *s = toupper(*s); s++; } /*メインループ*/ for(i = 3; i < argc; i++){ /*ファイルを開く*/ if((fp = fopen(argv[i], "r")) == NULL){ printf("error2"); exit(1); } /*文書を1行ずつ読み込んで処理を行う*/ while(fgets(buffer,sizeof(buffer),fp) != NULL){ /*最後に\0を格納*/ buffer[strlen(buffer) - 1] = '\0'; /*bufferの英小文字を英大文字に変換する処理を\0まで繰り返す*/ s = buffer; while(*s != '\0'){ *s = toupper(*s); s++; } /*strtokを用いて単語ごとに区切っていく(1単語目)*/ s = strtok(buffer,delimiter); if(s != NULL){ /*kueri1かどうか判定*/ if(strcmp(s,kueri1) == 0){ flag_kueri1 = 1; } /*kueri2かどうか判定*/ if(strcmp(s,kueri2) == 0){ flag_kueri2 = 1; } /*strtokを用いて単語ごとに区切っていく(1単語目)*/ while((s = strtok(NULL,delimiter)) != NULL){ /*kueri1かどうか判定*/ if(strcmp(s,kueri1) == 0){ flag_kueri1 = 1; } /*kueri2かどうか判定*/ if(strcmp(s,kueri2) == 0){ flag_kueri2 = 1; } } } /*kueriが両方なかったらflar_kueriを1にする*/ if(flag_kueri1 == 1 && flag_kueri2 == 1){ }else{ flag_kueri = 1; } /*両方のkueriがあればファイル名を出力*/ if(flag_kueri == 0){ s = argv[i]; printf("file_name: %s",s); } } fclose(fp); exit(0); }

みんなの回答

回答No.4

#2 です 判定は 関数fとできて 0または1を返せばよい while文のなかで  if (f(x) && f(y)) { 処理を行う } でかけてしまう。 オブジェクト指向言語で言われるリファクタリングの匂いはここでも有効です。重複行を避ける。 No3の方がいわれるのは、マジックナンバーを使うに近いかな。 それともう一点、一時的な変数を使わない。 フラグの変数を使わなくても 毎回関数を呼ぶ出せば、変数は使わなくてすむ。 なぜ変数を避けるかは、今の場合 毎回0に初期化されてないから、一度1を代入したら変更されない限り、1のまま。なぜなら、一番最初に定義されているので、while 文では同じスコープのまま! つまり、一時的期変数はこういうミスが起きてしまうから、使うなっていうわけ。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

「出力がうまくいきません」ってのは, 具体的には何がどう「うまくいかない」んですか? flag って名前はやめた方がいい. その「フラグ」が何を意味するのかを考え, その「意味」を表す名前を使うべき. 例えば query1_found みたいにすれば, コメントがなくても意味がわかるし, #1 で指摘されたような「矛盾」も起こしようがない.

回答No.2

プログラムの書き方に問題がありませんか? 似たような行が多すぎる、ということは、流れが追いにくいということ。 人に聞くよりも、プリントデバッグの手法を学びましょう。 そのためにも、重複行を整理しないと。 何のための、構造型言語なのか? 

mikoto1129
質問者

補足

回答ありがとうございます。 要するに、同じような処理を繰り返すのであれば 重複した記述ではなく関数などを使って書いた方がいいということでしょうか?

  • asuncion
  • ベストアンサー率33% (2126/6286)
回答No.1

kueriはqueryの方がいいのではないか、というのはさておき…。 >int flag_kueri = 0; //kueri1,kueri2がどちらもあれば1に >/*kueriが両方なかったらflar_kueriを1にする*/ >if(flag_kueri1 == 1 && flag_kueri2 == 1){ > >}else{ >flag_kueri = 1; >} 変数定義のところと実際の処理のところで、 flag_kueriの役割が矛盾しているように見えるのは気のせいでしょうか。

mikoto1129
質問者

補足

回答ありがとうございます。 確かにコメントアウトにミスがありました。 ご指摘ありがとうございます。 確かに、コメントアウトにミスはあったんですが処理自体は矛盾なく 両方、または片方のクエリが見つからなかった場合にはflag_kueriを1にすることで ファイル名の処理を避けるようにしようと思っているのですが if( flag_kueri1==1 && flag_kueri2==1){ }else{ flag_kueri=1; } とすることで、どちらか一方のクエリが見つからなかった場合はflag_kueriを1にする処理を行い、出力の際には if(flag_kueri == 0){ s=argv[i]; printf("file name: %s\n); } とすることで、flag_kueri=0の時、つまりはどちらか一方、または両方のクエリが見つからなかったことによりflag_kueriを1にする処理は行われずファイル名が出力されるようになっていると思うのですが、いかがでしょうか?

関連するQ&A

  • c言語を用いた文書検索に関する質問です。

    いつもお世話になっています。 c言語にあまり詳しくないので、どなたか詳しい方お願いします。 今回、ディレクトリの複数文書内で特定の単語(今回はクエリと呼びます)を含む文書を探し出すして、テキストファイル名を表示する処理を行うプログラムを作成していました。 以下に添付したソースは、コンパイル自体は通るのですがコマンドラインを ./ex12 "green" corpus/*.txt と入力するとsegmentation faultとなってしまいます。 (ex12は実行ファイル名、greenに特に意味はありません。別の単語でも同じ結果が出てしまいます。) 複数ファイルではなく1つの文書内での検索は問題なかったのですが、何が原因なのかがよく分かりません。 1つの文書内検索の際は、メインループの最初のfor文を除いての処理となります。 printfを用いていろいろ試した結果、ファイル自体はきちんと読み込めているようなのでfor文を入れたのが原因ではないようなのですが・・・。 ちょっと原因が分からなくて詰まってしまったので、どなたか詳しいかたよろしくお願いします。 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<ctype.h> int main(int argc, char* argv[]){ FILE* fp; char buffer[2048]; char kueri[256]; char* s; char* delimiter = " .,"; //単語の区切れの定義 int i; int flag = 0; /*コマンドラインが正しく入力されているかの確認*/ if(argc < 3){ printf("error1\n"); exit(1); } /*入力されたクエリをkueriに格納する*/ strcpy(kueri,argv[1]); /*英小文字を英大文字に変換する処理を\0まで繰り返す*/ s = kueri; while(*s != '\0'){ *s = toupper(*s); s++; } /*メインループ*/ for(i = 2; i < argc; i++){ /*ファイルを開く*/ if((fp = fopen(argv[i], "r")) == NULL){ printf("error2"); exit(1); } while(fgets(buffer,sizeof(buffer),fp) != NULL){ /*最後に\0を格納する*/ buffer[strlen(buffer)-1] = '\0'; /*英小文字を英大文字に変換する処理を\0まで繰り返す*/ s = buffer; while(*s != '\0'){ *s = toupper(*s); s++; } /*strtokを用いて単語毎に区切っていく(1単語目)*/ s = strtok(buffer,delimiter); if(strcmp(s,kueri) == 0){ printf("入力されたクエリを文書内に発見しました。\n"); printf("file name: %s\n",argv[i]); flag = 1; }else{ /*strtokを用いて単語毎に区切っていく(2単語目以降)*/ while((s = strtok(NULL, delimiter)) != NULL){ if(strcmp(s,kueri) == 0){ printf("入力されたクエリを文書内に発見しました。\n"); printf("file name: %s\n",argv[i]); flag = 1; } } } } fclose(fp); } if(flag == 0){ printf("クエリを文書内に発見できませんでした。\n"); } exit(0); }

  • c言語で文書を読み込み、単語の出現頻度を教える

    c言語の課題で、与えられた文書を読み込んでその中にある単語の出現頻度を教えるプログラミングを作成しているのですが、うまくいきません。 どこが間違っているのでしょうか?? ファイルの中身は As sweet as coat , green , milk And everyone think coat になっており、求めたい回答は buffer[0]=As count=1 buffer[1]=sweet count=1 buffer[2]=as count=1 buffer[3]=coat count=2 buffer[4]=green count=1 buffer[5]=milk count=1 buffer[6]=And count=1 buffer[7]=everyone count=1 buffer[8]=think count=1 Number of words:9 にしたいのです。 プログラミングは #include<stdio.h> #include<stdlib.h> #include<string.h> #include<ctype.h> typedef struct token_checker{ char * token; int count; }TOKEN_CHECKER; int main(int argc,char* argv[]){ FILE * fp; char buffer[1024]; int numword=0; char * delimiter = " .,"; char * s; TOKEN_CHECKER *t; int j; int i = 0; int flag =0; int find = -1; t=(TOKEN_CHECKER*)malloc(sizeof(TOKEN_CHECKER)*24); if(argc != 2){ printf("Parameter error.\n"); exit(1); } if((fp=fopen(argv[1],"r"))==NULL){ printf("File open error.\n"); exit(1); } while( fgets(buffer,sizeof(buffer),fp)!=NULL){ buffer[ strlen(buffer)-1]='\0'; s = strtok(buffer,delimiter); if(s != NULL){ t[numword].token=s; for(i=0;i<=numword;i++){ if(strcmp(t[numword].token,t[i].token)==0){ t[i].count++ } } printf("buffer[%d]=%s count=%d\n",numword,t[numword].token,t[numword].count); numword++; } while((s=strtok(NULL,delimiter)) != NULL){ t[numword].token=s; for(i=0;i<=numword;i++){ t[numword].count=0; if(strcmp(t[numword].token,t[i].token)==0){ t[i].count++; } } printf("buffer[%d]=%s count=%d\n",numword,t[numword].token,t[numword].count); numword++; } } printf("Number of words:%d\n",numword); free(t); fclose(fp); exit(0); } になっており、実行すると buffer[0]=As count=1 buffer[1]=sweet count=1 buffer[2]=as count=1 buffer[3]=coat count=1 buffer[4]=green count=1 buffer[5]=milk count=1 buffer[6]=And count=1 buffer[7]=everyone count=1 buffer[8]=think count=1 buffer[9]=coat count=1 Number of words:9 となってしまい、coatがカウントされないのです。

  • strtokについて

    strtokを使うにあって注意すべき点がしりたいのですが、 test1やtest2は書き換わるので、変わっては困る場合は あらかじめコピーしておくのは理解しましたが、 strtokをネストして使う場合、(1)で必ずNULLになります。 strtokはネストでは使えないのでしょうか? また、他に注意点があるのでしたら教えてください。 あと、C++では、strtokより便利なものはありますか? char test1[] = "111,222,333"; char test2[] = "333,222,111"; char *p, *q; int flag; p = strtok(test1, ","); while ( p != NULL ) { flag = 0; q = strtok(test2, ","); while ( q != NULL ) { if (0 == strcmp(p, q)) { flag = 1; break; } q = strtok( NULL, ","); } if (flag == 0) { return 1; } p = strtok( NULL, ","); // <------- (1)ここで必ずNULL }

  • 大文字を小文字に、小文字を大文字にするプログラム

    問題は、 ファイルにあるアルファベットの大文字を小文字に変換し、小文字は大文字に変換して、ファイルに保存するプログラムを作りなさい というものです。 色々考えて、 #include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #include <unistd.h> #include <ctype.h> int main(int argc,char **argv){ int fd,flag1,flag2; int i,n; char buffer[512],x; fd = open(argv[1],O_RDWR); if(fd == -1) perror("open"); while((n = read(fd,buffer,sizeof(buffer)))>0){ x = buffer[i]; flag1 = islower(x); flag2 = isupper(x); for(i=0;i<n;++i) if(flag1 == 1){ buffer[i] = toupper(buffer[i]); }else if(flag2 == 1){ buffer[i] = tolower(buffer[i]); } write(fd,buffer,n); } close(fd); exit(0); } のようなプログラムを考えましたが、うまくいきません。 toupperのような関数を使ったのは初めてなのでよく使い方が分からずこのようなプログラムになってしまいました。 どうか正しく動くようなプログラムを教えてください。お願いします。

  • C言語でファイルの内容を strtok関数 を使って数字と文字を分けて

    C言語でファイルの内容を strtok関数 を使って数字と文字を分けて配列に格納したいのですが、うまくできません。 どこが駄目なのかご指摘をお願いします! ファイル内容 20 田中 10 鈴木 #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { FILE *fp; char str[256]; char *tp; int i=0; int num[10]; char na[10]; fp=fopen(argv[1],"r"); while(fgets(str,sizeof str,fp)!=NULL); tp = strtok ( str, " " ); while(tp != NULL ) { num[i]=atoi(tp); tp = strtok( NULL," "); if ( tp != NULL ){ na[i]=*tp; } i++; } printf("%d\n%s",num[0],na[0]); printf("%d\n%s",num[1],na[1]); fclose(fp); return 0; }

  • プログラム高速化について

    http://oshiete1.goo.ne.jp/qa5810041.html で質問させていただいた者です。 ソースは下ので動いているのですが、北海道近辺の郵便番号を調べようとするとすぐに結果が返ってくるのですが、 沖縄県辺りですと少し時間がかかってしまいます。(わずかな差ですが…) 恐らく、上から1個ずつしらみつぶしに調べているのでそのような結果になっているのはわかるのですが… これを高速化するにはwhile文をいじる必要があると思うのですが、どのようにしたら良いでしょうか? 以下がプログラムになります。 ken_all.csvの場所:http://www.post.japanpost.jp/zipcode/dl/kogaki.html #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define NAME "ken_all.csv" #define SIZE 1024 #define setstr(x,z) {strcpy(x,strtok(z,",\""));} struct tb{ //構造体設定 char dummy[SIZE]; //全国地方公共団体コード char old_num[SIZE]; //旧郵便番号 char now_num[SIZE]; //現在の郵便番号 char kana1[SIZE]; //都道府県名(カナ) char kana2[SIZE]; //市区町村名(カナ) char kana3[SIZE]; //町域名(カナ) char kanji1[SIZE]; //都道府県名(漢字) char kanji2[SIZE]; //市区町村名(漢字) char kanji3[SIZE]; //町域名(漢字) }; int main(int argc, char *argv[]) { struct tb line; FILE *fp; char *address, buff[SIZE], string_buff[SIZE]; int flag; clock_t start,end; start = clock(); if(argc == 1){ printf("引数を指定してください\n"); } if(argc > 2){ printf("引数が多すぎます、引数は1つにしてください。\n"); return -1; } if((fp=fopen(NAME,"r"))==NULL){ printf("ファイル%sが開けません\n",NAME); return -1; } flag=0; address = argv[1]; while(fgets(buff,SIZE,fp) != NULL){ //各項目の設定 strcpy(line.dummy,strtok(buff,",\"")); strcpy(line.old_num,strtok(NULL,",\"")); strcpy(line.now_num,strtok(NULL,",\"")); strcpy(line.kana1,strtok(NULL,",\"")); strcpy(line.kana2,strtok(NULL,",\"")); strcpy(line.kana3,strtok(NULL,",\"")); strcpy(line.kanji1,strtok(NULL,",\"")); strcpy(line.kanji2,strtok(NULL,",\"")); strcpy(line.kanji3,strtok(NULL,",\"")); //文字の連結 strcpy(string_buff,line.kanji1); strcat(string_buff,line.kanji2); strcat(string_buff,line.kanji3); //住所の比較 if(strcmp(string_buff,address)==0){ printf("〒%s \n",line.now_num); flag=1; break; } //郵便番号の比較 if(strcmp(line.now_num,address)==0){ printf("%s \n",string_buff); flag=1; break; } } fclose(fp); if(flag==0 && atoi(argv[1]) == 0){ printf("「%s」に該当する郵便番号はありませんでした\n",address); } if(flag==0 && atoi(argv[1]) != 0){ printf("「%s」に該当する住所はありませんでした\n",address); } end = clock(); printf("%.30f秒かかりました\n",(double)(end-start)/CLOCKS_PER_SEC); return 0; }

  • C言語→C#に変換

    C言語→C#に変換 C言語からC#に変換したいのですが、わからないところがあります。 コマンドライン引数からファイル名とキーワードを入力して、キーワードがある行をアウトプットファイルに書き込むという処理です。 C言語のソースは、 main(int argc,char *argv[]){ FILE *fp; FILE *fpp; int i; char KEYWORD[256]; char buf[256]; //(1)アウトプットファイルのオープン fpp=fopen("output.txt","w"); for(i=1;i<argc-1;i++){ if(strcmp(argv[i],"-a")==0){ //(2)入力ファイルのオープン if((fp=fopen(argv[i],"r"))==NULL){ printf("open error!\n"); exit(1); } } //(3)キーワードの代入 if(strcmp(argv[i],"-b")==0){ strcpy(KEYWORD,argv[i]); } } //(4)一行読み込む while(fgets(buf,sizeof(buf),fp)!=NULL){ //(5)キーワードの条件で文字列抽出 if(strstr(buf,KEYWORD)!=NULL){ //(6)アウトプットファイルに出力 fprintf(fpp,"%s",buf); } } //ファイルクローズ fclose(fp); fclose(fpp); } です。 (3)キーワードの代入と、(5)文字列抽出の部分がネットなどで調べてもよくわかりません。 分かる方いらっしゃいましたら教えていただけると幸いです。 宜しくお願いします。 .

  • C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問

    C言語で、ファイルを読み込んで数字と名前に分けて配列に格納に関する質問です! ファイルを開いた後でエラーとなるのですが、何が足りないのでしょうか? ファイル内容 20 田中 10 鈴木 #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc,char *argv[]) { FILE *fp; char str[256]; char *tp; int k,i=0; int num[10]; char na[10][20]; fp=fopen(argv[1],"r"); if(fp==NULL){ printf("ファイルを開けません\n"); return 1; }else{ printf("開けた\n"); } while(fgets(str,sizeof str,fp)!=NULL){ tp=strtok(str," "); num[i]=atoi(tp); tp=strtok(NULL," "); strcpy(na[i],tp); i++; } printf("%d\n%s\n",num[0],na[0]); printf("%d\n%s\n",num[1],na[1]); fclose(fp); return 0; }

  • C言語に関する質問

    初めて質問させて頂きます。C言語初心者です。 実は講義で「ファイル中の英文を単語に分けてその出現頻度をカウントするコードを木構造を用いて出力せよ」という課題が出ました。 そこで、参考にするコードを検索しましたところ、以下のURLにあるベストアンサーのコードが近いと感じました。 http://okwave.jp/qa/q4155655.html コードの内容は以下の通りになります。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> typedef struct node Node; struct node{ char *word; int count; Node *left,*right; }; Node *root=NULL; void compose(FILE *fp); void inorder(Node *p); void strlower(char *s); int main(int argc, char *argv[]) { FILE *fp; Node *new; fp=fopen(argv[1],"r"); if(fp==NULL){puts("ファイルを開けません");return(-1);} compose(fp); inorder(root); return (0); } void strlower(char *s){ while(*s!=NULL){*s=tolower(*s);s++;} } void compose(FILE*fp){ Node **p,*new; char buf[256]; while(1){ fscanf(fp,"%[^a-zA-Z0-9]",buf); if(fscanf(fp,"%[a-zA-Z0-9]",buf)==EOF)break; strlower(buf); if(root==NULL){ new=(Node *)malloc(sizeof(Node)); new->left=NULL; new->right=NULL; new->word=strdup(buf); new->count=1; root=new; }else{ *p=root; while(1){ if(strcmp(buf,(*p)->word)==0){ (*p)->count++;break; }else if(strcmp(buf,(*p)->word)<0){ if((*p)->left==NULL){ new=(Node *)malloc(sizeof(Node)); new->left=NULL;new->right=NULL;new->word=strdup(buf);new->count=1; (*p)->left=new; break; }else{ *p=(*p)->left; } }else{ if((*p)->right==NULL){ new=(Node *)malloc(sizeof(Node)); new->left=NULL; new->right=NULL; new->word=strdup(buf); new->count=1; (*p)->right=new; break; }else{ *p=(*p)->right; } } } } } } void inorder(Node*p){ if (p==NULL) return; inorder(p->left); printf("%s %d\n",p->word, p->count); inorder(p->right); } しかし、これをそのままコンパイル・実行すると、コンパイル時に以下の注意が出ます。 warning comparison between pointer and integer ('int' and 'char *') while(*s!=NULL){*s=tolower(*s);s++;} 上記の注意を無視してそのまま実行すると、segmatation faultが出てしまいますorz おそらく、sの型が*s=s[]なので、注意の中の「s++」の部分で誤作動を起こしている(s++を実行するにはsはint型でなければならない)と思うのですが、どうコード文を変えれば良いのかがよくわかりません。 どなたかお教え頂けると幸いです。どうぞよろしくお願いしますm(_ _)m

  • C言語 strtok

    失礼します。現在こちらでアドバイスを頂きfgetcを使用して配列に格納をすることができたのですが、CSVをカンマ区切りで格納したいのですが上手くいかず困っています。strtokを使用方法をドキュメントを読んでもうまく区切ったものを配列に入れる方法がわかりません 何卒よろしくお願いします。 ソースコード #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include<string.h> #define MAXITEM 1400 int split(char *str, const char *delim, char *outlist[]) { char *tk; int cnt = 0; tk = strtok(str, delim); while (tk != NULL && cnt < MAXITEM) { outlist[cnt++] = tk; tk = strtok(NULL, delim); } return cnt; } int main(void) { FILE *fp; char *fname = "testfile.csv"; char *tp; char *array[1400]; char *test[11][1400]; char c; int i = 0; int n,y; char *tp[1400]; fp = fopen(fname, "r"); if (fp == NULL) { printf("%sファイルが開けません¥n", fname); return -1; } while ((c = fgetc(fp)) != EOF) { array[i] = (char)c; i++; } tp = strtok(array, ","); puts(*tp); while (tp != NULL) { tp = strtok(NULL, ","); if (tp != NULL)puts(tp); } for (n = 0; n < 11; n++) { for (y = 0; y < 1400; y++) { test[n][y] = tp[y]; printf("%c", test[n][y]); } } fclose(fp); return 0; }