ページ 1 / 1
入力した単語があるファイルを抽出してその頻度が多い順に並べたい
Posted: 2016年6月05日(日) 18:02
by rusty
まずは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);
}
Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい
Posted: 2016年6月05日(日) 18:20
by みけCAT
36行目、58行目、67行馬で初期化されていない不定の値のiが使用されています。
ここでiがたまたまトラップ表現だったり、添字として範囲外になる値だったりすると、未定義動作になってしまいます。
さらに、toupper関数にchar型の値を渡すと、
char型が符号付きの場合unsigned char型で表せる値でもEOFでもない値が渡されて未定義動作になる場合があるので、unsigned char型にキャストしてから渡すべきです。
Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい
Posted: 2016年6月05日(日) 19:14
by rusty
みけ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);
}
Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい
Posted: 2016年6月05日(日) 19:55
by みけCAT
rusty さんが書きました:unsigned charのキャストはどのようにすればいいのでしょうか
変換結果のintのcharへの変換は処理系定義になりますが、普通に書けばいいでしょう。
コード:
*s = toupper((unsigned char)*s);
Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい
Posted: 2016年6月05日(日) 20:28
by box
老婆心ながら…。
インデント(字下げ)をちゃんとするくせを付けておく方がいいと思います。
どの
{
と、どの
}
とが対応しているか、ご自分ですぐにわかりますか?
また、if文とかwhile文とかの中身が外に飛び出しているなんて、私に言わせればあり得ません。
Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい
Posted: 2016年6月05日(日) 21:20
by rusty
同じフォルダに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に調整してもらっているのですが書き方を変えたほうがいいのでしょうか(´・ω・`)
Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい
Posted: 2016年6月05日(日) 21:26
by みけCAT
テキストファイル名は2番目の引数なので、当然argv[1]ではなくargv[2]で参照しなければいけません。
Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい
Posted: 2016年6月05日(日) 22:00
by rusty
35、57、66行目をargv[2]にするとコアダンプ(´・ω:;.:...
Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい
Posted: 2016年6月05日(日) 22:23
by みけCAT
43行目でstrlen(buffer)が0でないか、55行目でsがNULLでないかを確認してください。
オフトピック
少しは自分でデバッグする努力をしているのかな…?
Re: 入力した単語があるファイルを抽出してその頻度が多い順に並べたい
Posted: 2016年6月06日(月) 02:50
by かずま
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' に変えなくて済みます。