入力した単語があるファイルを抽出してその頻度が多い順に並べたい

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

入力した単語があるファイルを抽出してその頻度が多い順に並べたい

#1

投稿記事 by rusty » 9年前

まずは1つのファイルに対しての検索を行いたいです
コンパイルは通るのですがコアダンプとなってしまいます…

コード:

#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);
}

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい

#2

投稿記事 by みけCAT » 9年前

36行目、58行目、67行馬で初期化されていない不定の値のiが使用されています。
ここでiがたまたまトラップ表現だったり、添字として範囲外になる値だったりすると、未定義動作になってしまいます。
さらに、toupper関数にchar型の値を渡すと、
char型が符号付きの場合unsigned char型で表せる値でもEOFでもない値が渡されて未定義動作になる場合があるので、unsigned char型にキャストしてから渡すべきです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

rusty

Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい

#3

投稿記事 by rusty » 9年前

みけCAT さんが書きました:36行目、58行目、67行馬で初期化されていない不定の値のiが使用されています。
ここでiがたまたまトラップ表現だったり、添字として範囲外になる値だったりすると、未定義動作になってしまいます。
さらに、toupper関数にchar型の値を渡すと、
char型が符号付きの場合unsigned char型で表せる値でもEOFでもない値が渡されて未定義動作になる場合があるので、unsigned char型にキャストしてから渡すべきです。
unsigned charのキャストはどのようにすればいいのでしょうか
初歩的なことをすみません

コード:

#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[1], "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[1]);
	flag = 1;
      }else{
	
	/*strtokを用いて単語毎に区切っていく(2単語目以降)*/
	while((s = strtok(NULL, delimiter)) != NULL){
	  
	  if(strcmp(s,kueri) == 0){
	    printf("入力されたクエリを文書内に発見しました。\n");
	    printf("file name: %s\n",argv[1]);
	    flag = 1;
	  }
	}
      }
    }
    
    fclose(fp);
    
    // }
  
  if(flag == 0){
    printf("クエリを文書内に発見できませんでした。\n");
  }
  
  exit(0);
}


アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい

#4

投稿記事 by みけCAT » 9年前

rusty さんが書きました:unsigned charのキャストはどのようにすればいいのでしょうか
変換結果のintのcharへの変換は処理系定義になりますが、普通に書けばいいでしょう。

コード:

*s = toupper((unsigned char)*s);
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

box
記事: 2002
登録日時: 14年前

Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい

#5

投稿記事 by box » 9年前

老婆心ながら…。
インデント(字下げ)をちゃんとするくせを付けておく方がいいと思います。

どの
{
と、どの
}
とが対応しているか、ご自分ですぐにわかりますか?

また、if文とかwhile文とかの中身が外に飛び出しているなんて、私に言わせればあり得ません。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

rusty

Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい

#6

投稿記事 by rusty » 9年前

同じフォルダにtxtファイルを用意して
./a.out "検索する単語" テキストファイル名

実行してもファイルを読み込んでくれません
何が原因なのでしょうか・・・

コード:

#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((unsigned char)*s);
    s++;
  }
  
  /*メインループ*/
  //  for(i = 2; i < argc; i++){
  
  /*ファイルを開く*/
  if((fp = fopen(argv[1], "r")) == NULL){
    printf("error2.\n");
    exit(1);
  }
  
  while(fgets(buffer,sizeof(buffer),fp) != NULL){
    
    /*最後に\0を格納する*/
    buffer[strlen(buffer)-1] = '\0';
    
    /*英小文字を英大文字に変換する処理を\0まで繰り返す*/
    s = buffer;
    while(*s != '\0'){
      *s = toupper((unsigned char)*s);
      s++;
    }
    
    /*strtokを用いて単語毎に区切っていく(1単語目)*/
    s = strtok(buffer,delimiter);
    
    if(strcmp(s,kueri) == 0){
      printf("入力されたクエリを文書内に発見しました。\n");
      printf("file name: %s\n",argv[1]);
      flag = 1;
    }else{
      
      /*strtokを用いて単語毎に区切っていく(2単語目以降)*/
      while((s = strtok(NULL, delimiter)) != NULL){
	if(strcmp(s,kueri) == 0)
	  {
	    printf("入力されたクエリを文書内に発見しました。\n");
	    printf("file name: %s\n",argv[1]);
	    flag = 1;
	  }
      }
    }
  }
  
  fclose(fp);
  // }
  if(flag == 0) printf("クエリを文書内に発見できませんでした。\n");
  
  exit(0);
}
インテンドはemacsに調整してもらっているのですが書き方を変えたほうがいいのでしょうか(´・ω・`)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい

#7

投稿記事 by みけCAT » 9年前

テキストファイル名は2番目の引数なので、当然argv[1]ではなくargv[2]で参照しなければいけません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

rusty

Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい

#8

投稿記事 by rusty » 9年前

35、57、66行目をargv[2]にするとコアダンプ(´・ω:;.:...

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい

#9

投稿記事 by みけCAT » 9年前

43行目でstrlen(buffer)が0でないか、55行目でsがNULLでないかを確認してください。
オフトピック
少しは自分でデバッグする努力をしているのかな…?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

かずま

Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい

#10

投稿記事 by かずま » 9年前

rusty さんが書きました:インテンドはemacsに調整してもらっているのですが書き方を変えたほうがいいのでしょうか(´・ω・`)
あなたのインデントは次のようになっています。

1. スペース 2個
2. スペース 4個
3. スペース 6個
4. タブ 1個
5. タブ 1個 + スペース 2個
6. タブ 1個 + スペース 4個

この掲示板では、タブをスペース 4個に置き換えるので、次のようになってしまいます。

1. スペース 2個
2. スペース 4個
3. スペース 6個
4. スペース 4個
5. スペース 6個
6. スペース 8個

expand コマンドで、タブをスペースに変換してから投稿してください。
rusty さんが書きました:35、57、66行目をargv[2]にするとコアダンプ(´・ω:;.:...
次のファイルで xyz を検索してもコアダンプしませんでした。

コード:

abc defghijkl
mno pqrstuvw xyz
987654321
質問するときは、入力データも付けたほうがいいでしょう。

strtok() の返却値は、必ず NULL かどうかチェックしましょう。
NULL のままそれを使って strcmp() するとコアダンプします。

strtok の書き方を工夫すると、同じことを 2度書かずに済みます。

コード:

#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 *p;
  char *s;
  char *delimiter = " \t\n\r\".,();:!?<>{}*+=#$%&";  //単語の区切れの定義
  int i;
  int flag = 0;

  if (argc < 3) {
    printf("error1.\n");
    exit(1);
  }
  strcpy(kueri, argv[1]);
  for (s = kueri; *s != '\0'; s++) {
    *s = toupper((unsigned char) *s);
  }
  for (i = 2; i < argc; i++) {
    if ((fp = fopen(argv[i], "r")) == NULL) {
      printf("error2.\n");
      exit(1);
    }
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
      for (s = buffer; *s != '\0'; s++) {
        *s = toupper((unsigned char) *s);
      }
      for (p = buffer; (s = strtok(p, delimiter)) != NULL; p = NULL) {
        if (strcmp(s, kueri) == 0) {
          printf("入力されたクエリを文書内に発見しました。\n"
                 "file name: %s\n", argv[i]);
          flag = 1;
        }
      }
    }
    fclose(fp);
  }
  if (flag == 0)
    printf("クエリを文書内に発見できませんでした。\n");
  exit(0);
}
delimiter に '\n' を追加すると、行の最後の '\n' を '\0' に変えなくて済みます。

閉鎖

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