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

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

ただいまの
回答率

88.92%

c言語で、「英文中に現れる単語とその出現回数を表示させるプログラム」の作成に困っています。

解決済

回答 5

投稿

  • 評価
  • クリップ 1
  • VIEW 945

nk-kong

score 3

前提・実現したいこと

c言語の問題です。「標準入力に与えられた文章の単語とその出現回数を出力せよ」という課題が解けなくて困っています。
例えば、標準入力で「this is a pen that is a pineapple」と入力されたら、
1 this
2 is
2 a
1 pen
1 that
1 pineapple
と出力される感じです。

ファイル名はword_histogram.cで、ターミナルはCygwin64を使っています。

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

$ ./word_histogram
this is a pen  that is a pineapple
test.sh: 23 行:  2011 終了                  cat $i.in
      2012 Segmentation fault      (コアダンプ) | ../$cmd $args > $i.out 2>&1
==== 期待される出力 (Correct output) ====
1       this
2       is
2       a
1       pen
1       that
1       pineapple
==== 出力の違い (Different lines) ====
--- 1.correct   2020-06-30 22:02:09.080436000 +0900
+++ 1.out       2020-07-07 12:46:38.603230400 +0900
@@ -1,6 +0,0 @@
-1      this
-2      is
-2      a
-1      pen
-1      that
-1      pineapple
================
テスト 1 失敗(failed)

テスト 1 に失敗しました. (A test case is failed)
プログラムを確認してください.(0)

該当のソースコード

#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
#define NUM_WORDS 20000

int main(){

  char w[30+1];
  char *word[NUM_WORDS];
  int count[NUM_WORDS];

  word[NUM_WORDS] = (char *)malloc(sizeof(char) * (strlen(w) + 1));
  if(word[NUM_WORDS] == NULL){
    fprintf(stderr, "Cannot allocate memory.\n");
    return 1;
  }

  int total = 0, i = 0, find = 0;

  while(scanf("%30s", w) != EOF){

      tolower(w[strlen(w)]);

      find = -1;
      for(i=0;i<total;i++){
        if(strcmp(*word[i],w)==0){
          find=i;
        }
      }

      if(find<0){
        strcpy(*word[total],w);
        count[total] = 1;
        total++;
      }
      else{
        count[find]++;
      }

  }

  for(i=0;i<total;i++){
    printf("%d\t%s\n", count[i], *word[i]);
  }

  free(word[NUM_WORDS]);
  return 0;

}

補足説明

・コンパイルはgcc -o word_histogram word_histogram.c
・実行は./word_histogram
・make testというコマンドで「すべてのテストに成功しました」と表示されれば、テスト成功。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

+2

ポインタや配列に関する基礎勉強がまだまだ必要ですね。
以下はどれも不当なメモリアクセスとなります。

word[NUM_WORDS] = (char *)malloc(sizeof(char) * (strlen(w) + 1));
tolower(w[strlen(w)]);
if(strcmp(*word[i],w)==0){
strcpy(*word[total],w);
printf("%d\t%s\n", count[i], *word[i]);

他にも、

  • 単語の大文字小文字は区別するのか? (This と thisなど)
    コードを読む限り、全て小文字扱いとするようですが、、、

各センテンスで「何をしたいのか」をコメント書きしましょう。
正しく動かなくても、やりたいことは(コメントで)書ける筈です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/07 15:00

    > また、現在のコードには、入力された文字列を単語単位に分割する処理がないようです。

    scanf が担当しています。

    キャンセル

  • 2020/07/07 15:16 編集

    this is a pen that is a pineapple
    これをどうやってscanfが分割するのですか?
    ・・・あっ! 空白がセパレータでしたね。失礼しました。

    キャンセル

  • 2020/07/08 00:26

    回答ありがとうございます。
    プログラミングを始めてまだ間もないので、まだまだ至らない点がございました。
    ご指摘いただいた箇所を、今後意識して取り組みたいと思います。

    キャンセル

checkベストアンサー

+1

同じ変数を使って書いてみましたが、ちょっと変わった書き方をしているので、
あくまでもこれは参考として、やり方を理解するだけにし、自分自身の書き方で
書き直してみてください。

#include <stdio.h>   // scanf, printf, fprintf
#include <stdlib.h>  // malloc, free
#include <ctype.h>   // tolower
#include <string.h>  // strcmp, strcpy

#define NUM_WORDS 20000

int main(void)
{
    char *word[NUM_WORDS], w[30+1];
    int count[NUM_WORDS], total = 0, i;

    while (scanf("%30s", w) == 1) {
        for (i = 0; w[i]; i++) w[i] = tolower((unsigned char)w[i]);
        for (i = 0; i < total && strcmp(word[i], w); i++) ;
        if (i == total) {
            if (i == NUM_WORDS)
                return fprintf(stderr, "Too many data.\n"), 1;
            if ((word[total] = malloc(strlen(w) + 1)) == NULL)
                return fprintf(stderr, "Cannot allocate memory.\n"), 2;
            strcpy(word[total], w), count[total++] = 1;
        }
        else count[i]++;
    }
    for (i = 0; i < total; i++)
        printf("%d\t%s\n", count[i], word[i]), free(word[i]);
    return 0;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/08 00:26

    回答ありがとうございます。
    このコードで実行したところ、うまくいきました。
    このコードを参考にして、自分なりにプログラミングしようと思います。

    キャンセル

+1

配列 word の各要素に、配列 w の大きさ分の容量を割り当てないと正常に動きません。
word[NUM_WORDS] = ...って、いきなり配列の領域外にアクセスしています。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/08 00:19

    回答ありがとうございます。
    確かにその通りですね。以後気を付けるようにします。

    キャンセル

+1

とりあえず。

word[NUM_WORDS] = (char *)malloc(sizeof(char) * (strlen(w) + 1));において
配列wordNUM_WORDS番目(の1つの箱)にmallocした結果を代入していますが、意図した動作でしょうか?(おそらく違うと思います)
また、配列wは初期化されておらずゴミ値が入っているのでstrlen(w)は30でもなく31でもなく出鱈目な結果が返ってきます。

またtolower(w[strlen(w)]);についてですが、意味、意図が不明です。
まずはtolowerの引数は何でどんな結果を返すのかリファレンスを読みましょう。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/08 00:21

    回答ありがとうございます。
    学校の教授のサンプルコードをそのまま使ったのが原因でした。

    キャンセル

+1

元のソースをまるっと無視して書いたやつ。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

typedef struct {
    int count;
    char *word;
} WordWithCount;

typedef struct {
    int length;
    int nextIndex;
    WordWithCount *list;
} WordWithCountList;

void init(WordWithCountList *words, int initSize);
int indexOf(WordWithCountList *list, char *token);
void add(WordWithCountList *words, char *token);
char *strlower(char *src);

const char *delimiters = " .,?!:;(){}[]\n";

int main(void)
{
    WordWithCountList words;

    init(&words, 16);

    char line[1000];
    fgets(line, sizeof(line), stdin);

    for (char *token = strtok(line, delimiters); token != NULL; token = strtok(NULL, delimiters)) {
        int foundIndex = indexOf(&words, token);
        if (foundIndex >= 0) {
            words.list[foundIndex].count++;
        } else {
            add(&words, token);
        }
    }

    for (int i = 0; i < words.nextIndex; i++) {
        printf("%d %s\n", words.list[i].count, words.list[i].word);
    }

    return 0;
}

void init(WordWithCountList *words, int initSize)
{
    words->nextIndex = 0;
    words->length = initSize > 0 ? initSize : 1;
    words->list = malloc(words->length * sizeof(WordWithCount));

    if (words->list == NULL) {
        fprintf(stderr, "words.list allocation (size: %d) is failed.\n", words->length);
        exit(1);
    }
}

int indexOf(WordWithCountList *words, char *token)
{
    for (int i = 0; i < words->nextIndex; i++) {
        if (strcasecmp(words->list[i].word, token) == 0) {
            return i;
        }
    }
    return -1;
}

void add(WordWithCountList *words, char *token)
{
    words->list[words->nextIndex].word = strlower(token);
    words->list[words->nextIndex].count = 1;
    words->nextIndex++;

    if (words->nextIndex >= words->length) {
        words->length *= 2;
        words->list = realloc(words->list, words->length * sizeof(WordWithCount));
        if (words->list == NULL) {
            fprintf(stderr, "wordList re-allocation (size: %d) is failed.\n", words->length);
            exit(2);
        }
    }
}

char *strlower(char *src)
{
  char *ptr = src;
  while (*ptr != '\0') {
    *ptr = tolower(*ptr);
    ptr++;
  }
  return src;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • Cに関する質問
  • c言語で、「英文中に現れる単語とその出現回数を表示させるプログラム」の作成に困っています。