質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Q&A

4回答

746閲覧

C  単語の出現頻度を調べる

ambitious

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

0グッド

0クリップ

投稿2022/07/07 02:52

編集2022/07/07 17:06

前提

C言語で文章中の出現頻度を計算、表示しさらに標準入力されたwordの出現頻度を表示するプログラムを作っています。

実現したいこと

, . ' : ; ( )  空白(スペース)などの特殊文字で区切る。
大文字は小文字にする
文章中の単語を二分探索木に格納する。
出現頻度の計算、表示、検索

発生している問題・エラーメッセージ

出力結果が想定通りでない

89 apple
23 grape
2 lemon
1200種類の単語を登録
としたいです。

該当のソースコード

C

試したこと

出力結果が想定通りになりません。

補足情報(FW/ツールのバージョンなど)

gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ambitious

2022/07/07 02:59

元のプログラムではファイル名は実在するファイルにしてあり、開くことが出来ます。
otn

2022/07/07 03:13 編集

segmentation fault は、どの行でですか?
tatsu99

2022/07/07 03:23

ファイルの内容を提示すると、良い回答がつきやすくなります。
melian

2022/07/07 03:33

以下の部分で、c がアルファベットの場合は無限ループになって、str[i] = c; で範囲外アクセスが発生しているのでしょう。 while (c != 32 && c != 44 && c != 46 && c != 58 && c != 59 && c != 28 && c != 29) { if (65 <= c && c <= 90) c += 32; str[i] = c; i++; }
ambitious

2022/07/07 05:02

指摘箇所を修正し、faultは出なくなりましたが出力結果が想定通りではありません。
ambitious

2022/07/07 05:22

************************** 入力ファイル名: *.txt � *** 単語の出現頻度 *** 1 � 0種類の単語を登録 単語, または00を入力して下さい(00なら終了します): 出力結果です。
ambitious

2022/07/07 05:23

Section 1. Full faith and credit shall be given in each state to the public acts, records, and judicial proceedings of every other state. And the Congress may by general laws prescribe the manner in which such acts, records, and proceedings shall be proved, and the effect thereof. Section 2. The citizens of each state shall be entitled to all privileges and immunities of citizens in the several states. A person charged in any state with treason, felony, or other crime, who shall flee from justice, and be found in another state, shall on demand of the executive authority of the state from which he fled, be delivered up, to be removed to the state having jurisdiction of the crime. 読み込むファイルの一部です。
jimbe

2022/07/07 08:48 編集

main の最初の(fgetc をしている) while 文の中で、変数 i がどのような変化をするか確認しては如何でしょうか。 なお、ご提示のコードでは何か所かで return が足りません。
guest

回答4

0

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <ctype.h> 5 6#define SEPARATORS ",.':;()" 7 8typedef struct cell { 9 char key[20]; // 単語 10 int count; // 単語の登録回数(出現頻度) 11 struct cell *left, *right; 12} CELL; 13 14#define TBRANCH(a,p) ((a) > 0 ? &(p)->right : &(p)->left) 15 16/* 2分探索木に単語を登録する関数 */ 17CELL* tinsert(CELL *p, char *name) { 18 if(p == NULL) { 19 p = malloc(sizeof(CELL)); 20 strcpy(p->key, name); 21 p->count = 1; 22 p->left = p->right = NULL; 23 return p; 24 } 25 26 int a = strcmp(name, p->key); 27 if(a == 0) { 28 p->count ++; 29 } else { 30 CELL **q = TBRANCH(a, p); 31 *q = tinsert(*q, name); 32 } 33 return p; 34} 35 36/* 間順走査を行う関数 */ 37int inorder(CELL *p) { 38 if(p == NULL) return 0; 39 40 int count = inorder(p->left); 41 printf("%d %s\n", p->count, p->key); 42 return count + 1 + inorder(p->right); 43} 44 45/* 2分探索木を探索する関数 */ 46CELL* tsearch(CELL *p, char *name) { 47 if(p == NULL) return NULL; 48 49 int a = strcmp(name, p->key); 50 if(a == 0) return p; 51 return tsearch(*TBRANCH(a, p), name); 52} 53 54CELL *tread(char *filename) { 55 printf("\n**************************\n"); 56 printf("入力ファイル名: %s\n\n", filename); 57 58 FILE *fp; 59 if((fp = fopen(filename, "r")) == NULL) { 60 printf("File open error \n"); 61 return NULL; 62 } 63 64 CELL *root = NULL; 65 int c, i = 0; 66 char str[256]; 67 while((c = fgetc(fp)) != EOF) { 68 if(c > ' ' && !strchr(SEPARATORS, c)) { 69 str[i++] = tolower(c); 70 str[i] = '\0'; 71 continue; 72 } 73 if(i == 0) continue; 74 75 printf("%s\n", str); 76 root = tinsert(root, str); 77 i = 0; 78 } 79 fclose(fp); 80 81 return root; 82} 83 84void tfree(CELL *p) { 85 if(p == NULL) return; 86 87 tfree(p->right); 88 tfree(p->left); 89 free(p); 90} 91 92int input(char *buf) { 93 printf("\n単語, または00を入力して下さい(00なら終了します): "); 94 scanf("%s", buf); 95 return strcmp(buf, "00") != 0; 96} 97 98int main(void) { 99 CELL *root = tread("test.txt"); 100 if(root == NULL) exit(1); 101 102 printf("*** 単語の出現頻度 ***\n"); 103 printf("%d種類の単語を登録\n", inorder(root)); 104 105 for(char name[256]; input(name); ) { 106 CELL *p = tsearch(root, name); 107 if(p == NULL) printf("%sの単語は登録されていません!\n", name); 108 else printf("%sの出現頻度: %d\n", name, p->count); 109 } 110 printf("終了します.\n"); 111 112 tfree(root); 113 exit(0); 114}

実行結果

plain

1************************** 2入力ファイル名: test.txt 3 4section 51 6full 7faith 8and 9credit 10shall 11be 12given 13in 14 : 15(中略) 16 : 17be 18removed 19to 20the 21state 22having 23jurisdiction 24of 25the 26crime 27*** 単語の出現頻度 *** 281 1 291 2 301 a 312 acts 321 all 337 and 341 another 351 any 361 authority 376 be 38 : 39(中略) 40 : 411 states 421 such 4310 the 441 thereof 454 to 461 treason 471 up 482 which 491 who 501 with 5168種類の単語を登録 52 53単語, または00を入力して下さい(00なら終了します): the 54theの出現頻度: 10 55 56単語, または00を入力して下さい(00なら終了します): abc 57abcの単語は登録されていません! 58 59単語, または00を入力して下さい(00なら終了します): 00 60終了します.

投稿2022/07/07 19:04

編集2022/07/08 03:35
jimbe

総合スコア12648

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

1.ファイル名(sfn)が".txt"担っているが、C言語ではワイルドカードを指定して読み込むことはできません。
具体的な実際に存在するファイル名を指定しなければいけません。(word.txtとしました)
それともファイル名を公開したくなくて
.txtとしたのでしょうか。
いずれにせよ、*.txtではエラーになります。

2.char c は一般論として、int型で確保したほうが良いです。
(getcの戻り値がint型のためです。EOFの場合、-1が返り、文字を読み込んだ場合は0~255の値が返ります)
ファイル内の文字が全てASCII(7ビット)なら、問題ありませんが、漢字を含む場合、0x80以上の文字が返り
char 型で扱うと、0x80以上の場合対応できなくなります。
(今回の場合はファイル内の文字が全てASCII(7ビット)なのでchar型でも対応は可能です)

3.char str[256]の初期化が行われていません。

4.特殊文字が出現した場合を判定するのではなく、A-Z,a-z以外の文字が出現した場合を、
区切り文字として扱うべきです。いくつか区切り文字をならべてますが、タブキー、改行コード等がいろいろ
漏れています。
従って、
①A-Z,a-zの場合、単語としてstrに格納
②上記以外の場合、strが空でなければ、tinsertを呼び出して単語登録
のようにすべきです。

5.tinsertで、p->right,p->leftがNULLの時、mallocして単語を登録しますが、
そのmallocしたアドレスが設定されていません。

6.inorderのカウントの方法がおかしいので修正しました。

以上を反映すると以下のようになります。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5struct node 6{ 7 char key[20]; // 単語を格納 8 int count; // 単語の登録回数(出現頻度)を格納 9 struct node *left, *right; 10}; 11typedef struct node CELL; 12 13CELL *tinsert(CELL *, char *); /* 2分探索木に単語を登録する関数 */ 14int inorder(CELL *); /* 間順走査を行う関数 */ 15CELL *tsearch(CELL *, char *); /* 2分探索木を探索する関数 */ 16 17int main(void) 18{ 19 CELL *root, *p, *q; 20 char name[20]; 21 char sfn[] = "word.txt"; //修正 22 FILE *sfp; 23 int c; //修正 24 char str[256]; 25 int a, i, j = 0; 26 if ((sfp = fopen(sfn, "r")) == NULL) 27 { 28 printf("File open error \n"); 29 exit(1); 30 } 31 32 printf("\n**************************\n"); 33 printf("入力ファイル名: %s\n\n", sfn); 34 //追加開始 35 for (i = 0; i < 20; i++) 36 { 37 str[i] = '\0'; //初期化 38 } 39 //追加終了 40 root = NULL; 41 i = 0; 42 while ((c = fgetc(sfp)) != EOF) 43 { 44 //修正開始 45 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) 46 { 47 if (c >= 'A' && c <= 'Z') 48 c += 32; 49 str[i] = c; 50 i++; 51 continue; 52 } 53 else 54 { 55 if (strcmp(str,"") == 0) continue; 56 } 57 //修正終了 58 if (j == 0) //修正 59 { 60 printf("%s\n", str); //修正 61 root = tinsert(root, str); 62 j++; 63 for (i = 0; i < 20; i++) 64 { 65 str[i] = '\0'; //初期化 66 } 67 i = 0; 68 } 69 else if (j == 1) 70 { 71 printf("%s\n", str); //修正 72 q = tinsert(root, str); 73 for (i = 0; i < 20; i++) 74 { 75 str[i] = '\0'; //初期化 76 } 77 i = 0; 78 } 79 } 80 81 printf("*** 単語の出現頻度 ***\n"); 82 83 a = inorder(root); 84 printf("%d種類の単語を登録\n", a); 85 86 while (1) 87 { 88 printf("\n単語, または00を入力して下さい(00なら終了します): "); /*** 00 が入力されるまで繰り返す***/ 89 scanf("%s", name); 90 if (strcmp(name, "00") == 0) 91 break; 92 else 93 { 94 q = tsearch(root, name); 95 if (q != NULL) 96 printf("%sの出現頻度: %d\n", name, q->count); 97 } 98 } 99 printf("終了します.\n"); 100 101 fclose(sfp); 102 103 return 0; 104} 105 106CELL *tinsert(CELL *p, char *name) 107{ 108 int ind; 109 CELL *wp; //追加 110 if (p == NULL) 111 { 112 p = (CELL *)malloc(sizeof(CELL)); 113 strcpy(p->key, name); 114 p->count = 1; 115 p->left = p->right = NULL; 116 return p; 117 } 118 //修正開始 119 else if (strcmp(name, p->key) > 0){ 120 wp = tinsert(p->right, name); 121 if (p->right == NULL){ 122 p->right = wp; 123 } 124 }else if (strcmp(name, p->key) < 0){ 125 wp = tinsert(p->left, name); 126 if (p->left == NULL){ 127 p->left = wp; 128 } 129 //修正終了 130 }else if (strcmp(name, p->key) == 0){ 131 p->count += 1; 132 return p; 133 } 134} 135int inorder(CELL *p) 136{ 137 int count = 0; 138 //修正開始 139 if (p != NULL) 140 { 141 count++; 142 if (p->left != NULL){ 143 count += inorder(p->left); 144 } 145 if (p->right != NULL){ 146 count += inorder(p->right); 147 } 148 } 149 return count; 150 //修正終了 151} 152CELL *tsearch(CELL *p, char *name) 153{ 154 int ind; 155 156 if (p != NULL) 157 { 158 if (strcmp(name, p->key) > 0) 159 tsearch(p->right, name); 160 else if (strcmp(name, p->key) < 0) 161 tsearch(p->left, name); 162 else if (strcmp(name, p->key) == 0) 163 return p; 164 } 165 166 else 167 { 168 printf("%sの単語は登録されていません!\n", name); 169 return NULL; 170 } 171} 172 173

投稿2022/07/07 11:17

tatsu99

総合スコア5447

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

最初の while文が無限ループになります。

追記
修正版の && 48 > c && c > 57 で、そんな c は存在せず、
str[i] = c; は実行されません。
また、48 や 57 と書かずに '0' や '9' と書いたほうがいいでしょう。

投稿2022/07/07 03:31

編集2022/07/07 06:29
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ambitious

2022/07/07 05:07

ご回答ありがとうございます。 修正版を上げましたので、ご参照よろしくお願いいたします。
guest

0

まず、

if ((sfp = fopen(sfn, "r")) == NULL)

ファイル名が不正ですね

投稿2022/07/07 02:57

y_waiwai

総合スコア87774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問