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がカウントされないのです。
No.1
- 回答日時:
1)単語をトークンに切ったあと、
bufferに入っているトークンを探してあれば+1なのに、
無条件に
t[numword].token=s; で、トークンを登録してしまっている。
t[numword].count=0; で0にして
t[i].count++; で1になる。
なので、
for(i=1;i<=numword;i++){ まず探して、(要素0は使わない)
if(strcmp(t[numword].token,t[i].token)==0){ あれば
t[i].count++; +1
break; ループを抜ける。
}
}
if(i==numword){ なければ
numword++;
t[numword].token=s; 登録して
t[numword].count=1; カウント1
}
2)printf("buffer[%d]=%s count=%d\n",numword,t[numword].token,t[numword].count);
せっかく探して+1するようにしているのに、トークンが出るたびにprintfしている。
全てのトークンの処理が終わってからまとめてprintfしないといけない。
上記2つで処理はできると思います。
あと、改善するなら
3)24個までにするなら25個めが出てきたらメモリーを壊すので24個登録したら処理を終わる。
もっと言えばトークンが出てくるたびに
t=(TOKEN_CHECKER*)malloc(sizeof(TOKEN_CHECKER));
で1個づつメモリーを確保してつないでいくようにすると個数の制限はなくなる。
すぐの回答ありがとうございました。
早速直してみたんですが、セグメンテーションとなってしまいました。
maikoさんの通りに直してみたプログラミングは
while( fgets(buffer,sizeof(buffer),fp)!=NULL){
buffer[ strlen(buffer)-1]='\0';
s = strtok(buffer,delimiter);
if(s != NULL){
for(i=1;i<=numword;i++){
if(strcmp(t[numword].token,t[i].token)==0){
t[i].count++;
break;
}
}
if(i == numword){
numword++;
t[numword].token = s;
t[numword].count = 1;
}
numword++;
}
while((s=strtok(NULL,delimiter)) != NULL){
for(i=1;i<=numword;i++){
if(strcmp(t[numword].token,t[i].token)==0){
t[i].count++;
break;
}
}
if(i == numword){
numword++;
t[numword].token = s;
t[numword].count = 1;
}
}
numword++;
}
for(i=0;i<=numword;i++){
printf("buffer[%d]=%s count=%d\n",i,t[i].token,t[i].count);
}
としてみましたが、何か物足りない所があるでしょうか??
すごい参考になったので回答してくれてうれしかったです。
ありがとうございます
No.2
- 回答日時:
いくつか問題があります。
(1) あなたの実行結果のcountは全て1になっていますが私の実行結果は1にならないところがあります。
それは構造体TOKEN_CHECKERの配列tのcountの初期化が抜けているところがあるからです。
(2) 以下が変です。
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++;
}
配列tの既に格納済みの部分と新しく最後に追加した部分のtokenを比較して一致したらcountに1を加算していますけど、途中で一致するものがあろうとなかろうと最後まで比較しています。そして更に途中で一致するものがあろうとなかろうとnumwordはインクリメントされます。
だから buffer[3]とbuffer[9]は共にcoatという結果になっています。
(3) 私の実行結果はNumber of words:10となりました。
回答ありがとうございます。
for(i=1;i<=24;i++){
t[i].count = 0;
}
を処理する前につけて、前に回答してくださった方と参考しながら実行してみましたが、セグメンテーションと出てしまいました。
tokenは初期設定しなくても大丈夫でしょうか??
No.3
- 回答日時:
私の指摘では足りませんでした。
(間違いもm(__)m)で、テストして動くソースを載せておきます。
#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 i;
int find;
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){
find=0;
for(i=1;i<=numword;i++){
if(strcmp(s,t[i].token)==0){
t[i].count++;
find=1;
}
}
if(find==0){
numword++;
t[numword].token=(char*)malloc(strlen(s)+1);
strcpy(t[numword].token,s);
t[numword].count=1;
}
}
while((s=strtok(NULL,delimiter)) != NULL){
find=0;
for(i=1;i<=numword;i++){
if(strcmp(s,t[i].token)==0){
t[i].count++;
find=1;
}
}
if(find==0){
numword++;
t[numword].token=(char*)malloc(strlen(s)+1);
strcpy(t[numword].token,s);
t[numword].count=1;
}
}
}
for(i=1;i<=numword;i++){
printf("buffer[%d]=%s count=%d\n",i,t[i].token,t[i].count);
free(t[i].token);
}
printf("Number of words:%d\n",numword);
free(t);
fclose(fp);
exit(0);
}
ソースまで書いていただきありがとうございます。
私のソースとmaikoさんのソースを見比べたときに
なるほど!!と思ったところがたくさんありました。
私の理解力が低くてすいません…。
わざわざありがとうございました。
No.4ベストアンサー
- 回答日時:
まずは、「設計図」をきちんと作りましょう。
・合計を表示するには、集計が全て終了してからでなければなりません。
途中で出力できるのは、途中経過だけです。
よって、プログラムは
単語毎に数を数える
↓
単語毎の数と単語数を出力する
という(最低でも)2部構成になっている必要があります。
このプログラムは、一単語毎に、その単語の表示をしています。
・集計のロジックが間違っています。
もし 既に登録されている単語だったら
その単語の count++
そうでなかたら
新規に単語を登録
count=1
以上を、ファイル全体で繰り返す
集計部分のロジックはこうなると思います。
御自身のプログラムを、このように日本語に置き換えてみたら、期待通りに集計してくれそうですか?
紙と鉛筆を用意して、御自身がコンピュータになったつもりで、このプログラムの通りに「実行」してみるのもよいでしょう。
・ポインタと文字列の違いを理解しましょう。
C言語でもっとも難しいところではあるのですが
s = strtok(buffer,delimiter);
t[numword].token=s;
他の言語だと、t[numword].token で記録する文字列が s と同じ内容になる、という場合があります。
しかし、このプログラムは 「t[numword].token で記録するポインタ(アドレス)が、 s のポインタの値と同じになる」ものです。
具体的に言うと。
char buffer[1024];
これで、配列 buffer が用意されました。このとき、100番地から確保されたとします。
bufferに1行読み込むと
100番地: A
101番地: s
102番地:
103番地: s
104番地: w
105番地: e
...
という状態になります。
s = strtok(buffer,delimiter);
で、トークンが見付かった場合、sはその先頭アドレスを返します。この例ならば、As の先頭の100番地です。
t[numword].token=s;
とすると、 t[numword].tokenには「100番地」が入ります。
「Asという文字列」ではありません。
ここで、問題になるんは、次の行をbufferに読み込んだときです。
bufferに1行読み込むと
100番地: c
101番地: o
102番地: a
103番地: t
104番地: ,
...
となります。
すると、さっき「As」を覚えさせたつもりが、100番地からの文字列が「As」ではなくなってしまっています。
ずっと覚えさせておくには
tokenを 配列にして、strcpyで記録する
tokenを ポインタのまま、mallocで領域を確保して、strcpyで記録する
tokenを ポインタのまま、bufferではなく毎回の領域に読み込むようにする
等の工夫が必要です。
まだプログラミングについてはあまり詳しくないほうなので、
文字列がどうやってアドレス番地に格納されるかなど
丁寧に教えていただきありがとうございます。
あとで自分でどういう構成で作りたかったのかを手書きで書いてみたいと思います。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# プログラミング c言語 4 2023/03/07 01:05
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
- C言語・C++・C# C言語 3 2022/11/09 13:27
- Ruby 【JAVA】数字をひし形に出力するプログラムについて 2 2022/07/11 23:32
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# バイナリファイルをコピーするのにかかる時間を測りたいのですが実行するとFatel error:gli 2 2022/11/03 01:10
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- Visual Basic(VBA) エクセル マクロ(A1:A10)までの中で一番多く出た数字をB10に表示 6 2023/04/25 17:01
- Visual Basic(VBA) A列B列C列 3 2023/04/26 18:11
- UNIX・Linux 次の要件を満たすにはどのように修正したらよろしいでしょうか 1 2022/11/24 20:57
このQ&Aを見た人はこんなQ&Aも見ています
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Enterキーを押されたら次の処理...
-
【C言語】全角文字の配列を、全...
-
プログラミングの授業の課題です
-
C言語のプログラムで、途中で止...
-
C言語でファイルから複素数の値...
-
終了条件Ctrl+zについて,結果表...
-
空白を含んだ文字列がうまく格...
-
printf による16進表示について
-
警告 W8075 問題のあるポインタ...
-
VC++でSQLへSELECT文を送ったの...
-
C言語でのCSVファイルの読み出...
-
へんな現象
-
#define _CRT_SECURE_NO_WARNIN...
-
マイナスからプラスへ転じた時...
-
*をユーザーが入力した数字の数...
-
2÷3などの余りについて
-
10個出力で改行したいのですが...
-
C言語での奇数の和
-
複数桁10進数の*桁目だけを抽出...
-
正負を反転させて出力するプロ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Enterキーを押されたら次の処理...
-
C言語で複数列のデータを1列の...
-
#defineが使用するメモリ領域に...
-
printf による16進表示について
-
空白を含んだ文字列がうまく格...
-
C言語のプログラムで、途中で止...
-
プログラミングの授業の課題です
-
char型2つを結合し、short型に...
-
C言語でのCSVファイルの読み出...
-
矢印キーを押下してコンソール...
-
【C言語】全角文字の配列を、全...
-
エラーについて質問です。
-
リストの作成と出力(C言語)
-
バイナリファイル(画像)のよみ...
-
[C]セグメンテーションエラー:...
-
WinInetのInternetOpenUrl関数...
-
c言語で文書を読み込み、単語の...
-
VC++でSQLへSELECT文を送ったの...
-
受信データから必要な部分のみ...
-
reallocでエラー
おすすめ情報