使用言語 C 環境 Visual Studio 2017
初めての質問です。
独学でプログラミングを始めたのですがわからないところがあり困っています。
char型の配列変数の使い方がいまいちわかりません。
キーボードからいくつかの単語を半角スペースで区切って入力し、最後に改行を行うと
それらの単語を単語ごとに入力された順に、単語と入力回数を出力する
というプログラムを書きたいです。
英単語です。
入力例:
cat dog dog bird cat dog
出力例:
cat 2
dog 3
bird 1
・どこが間違っているのか
・違う書き方(便利な手法があれば)
の二点を教えていだたきたいです。
以下は自分で書いてみたプログラムとエラー箇所です。
#include <stdio.h>
#include <string.h>
int main(void) {
char ward[1001]; //単語 字数は 1<=wards<=1000//
int i, j;
int ct = 1; //入力された単語の数
char list_W[1001][1001]; //単語リスト
int ct_Same_W[1001]; //同単語カウント
//半角スペースで区切られて単語(半角英字)が標準入力される(末尾は改行)
int lp;
while(lp == 1) {
scanf("%s", ward); //単語の入力
for (j = 0;j < ct;j++) {
if (list_W[j] == ward) { //同単語の場合、ct_Same_W[j]でカウント
ct_Same_W[j] += 1;
}
else if (j == ct - 1) { //同単語がない場合はlist_W[j]へ代入
list_W[j] = ward;
ct += 1;
}
}
for (i = 0;i <= strlen(ward);i++) {
if (ward[i] == '\n') { //改行があった場合、
lp == 0; //ループを離脱
}
}
}
for (i = 0;i <= j;i++) {
//単語リストとそれぞれの出現回数を表示
printf("%s %d\n", list_W[i], ct_Same_W[i]);
}
}
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
そもそも文字列操作をCでやるな、という話がありますが、それでもCでやってみましょう。
個人的にscanf
が好きじゃないので、fgets
とstrtok
を組み合わせてやってみましょう。
strtok
は非常に癖があり、スレッド安全ではなく、入力文字列を書き換えるので使いにくいですが、今回は利用できそうです。
c
1#include <stdio.h> 2#include <string.h> 3#include <assert.h> 4 5#ifndef countof 6# if defined(_MSC_VER) && !defined(__c2__) 7 //MSVC except Clang CodeGen 8# define countof _countof 9# else 10# define countof( arr ) (sizeof(arr) / sizeof(*arr)) 11# endif 12#endif 13 14///! 単語情報を格納する構造体 15typedef struct tag_word_info { 16 ///! 見つかった数 17 size_t count; 18 ///! 単語 19 char word[1001]; 20} word_info; 21 22int main(void) { 23 char line[1001]; 24 size_t ct = 0; //入力された単語の数 25 word_info word_list[1001];//単語リスト 26 //半角スペースで区切られて単語(半角英字)が標準入力される(末尾は改行) 27 if(NULL == fgets(line, countof(line), stdin)) return 1; 28 for(const char* word = strtok(line, " \n"); NULL != word; word = strtok(NULL, " \n")) { 29 size_t i; 30 for (i = 0; i < ct && ct < countof(word_list); ++i) { 31 if (0 == strcmp(word_list[i].word, word)) { 32 //同単語の場合、ct_Same_W[i]でカウント 33 ++word_list[i].count; 34 break; 35 } 36 } 37 if(i == ct) { 38 //同単語がない場合はlist_W[i]へコピー 39 //strcpy(word_list[i].word, word); 40 memcpy(word_list[i].word, word, strlen(word) + 1); 41 word_list[i].count = 1; 42 ++ct; 43 } 44 } 45 for (size_t i = 0; i < ct; ++i) { 46 //単語リストとそれぞれの出現回数を表示 47 printf("%s %zd\n", word_list[i].word, word_list[i].count); 48 } 49} 50
https://wandbox.org/permlink/AdnCtvSQJx5d32MM
それではいくつか。
- 変数の命名、もうすこしどうにかなりませんかね・・・
- 配列の添字として使う変数は原則
size_t
型にしましょう - 文字列中から文字検索は自分でループを書かずに、
strchr
関数を利用しましょう(今回はstrtok
でやってしまいましたが) - 構造体をつかうと可読性が上がることがあります。
追記
もうすこし関数わけするべきだったという反省を活かし、C言語でオブジェクト指向してみました。C++書きたい・・・
C
1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <assert.h> 5 6#ifndef countof 7# if defined(_MSC_VER) && !defined(__c2__) 8 //MSVC except Clang CodeGen 9# define countof _countof 10# else 11# define countof( arr ) (sizeof(arr) / sizeof(*arr)) 12# endif 13#endif 14 15///! 単語情報を格納する構造体 16typedef struct tag_word_info { 17 ///! 見つかった数 18 size_t count; 19 ///! 単語 20 char word[1001]; 21} word_info; 22 23typedef struct tag_word_list word_list; 24size_t word_list_find_word(const word_list* words, const char* key_str); 25void word_list_print(const word_list* words); 26void word_list_push_back(word_list* words, const char* word); 27struct tag_word_list { 28 size_t (*find_word)(const word_list* words, const char* key_str); 29 void (*print)(const word_list* words); 30 void (*push_back)(word_list* words, const char* word); 31 ///! 現在の大きさ(1001以下) 32 size_t size; 33 ///! リスト 34 word_info list[1001]; 35}; 36 37/** 38 * @brief find word from list 39 * @params words[in] read-only pointer to word list 40 * @params key_str[in] read-only pointer to key string to find. 41 * @return Found index. If not found, this function will return `words->size`. 42 */ 43size_t word_list_find_word(const word_list* words, const char* key_str){ 44 size_t i; 45 for (i = 0; i < words->size; ++i) { 46 if (0 == strcmp(words->list[i].word, key_str)) return i; 47 } 48 return i; 49} 50/** 51 * @brief print-out word list 52 * @params words[in] read-only pointer to word list 53 */ 54void word_list_print(const word_list* words){ 55 for (size_t i = 0; i < words->size; ++i) { 56 //単語リストとそれぞれの出現回数を表示 57 printf("%s %zd\n", words->list[i].word, words->list[i].count); 58 } 59} 60/** 61 * @brief append new word to word list 62 * @params words[in,out] pointer to word list 63 * @params word[in] Read-only pointer to string to append. 64 */ 65void word_list_push_back(word_list* words, const char* word){ 66 if(countof(words->list) <= words->size) return; 67 memcpy(words->list[words->size].word, word, strlen(word) + 1); 68 words->list[words->size].count = 1; 69 ++words->size; 70} 71/** 72 * @brief create word list 73 */ 74word_list* new_word_list(){ 75 word_list* re = (word_list*)malloc(sizeof(word_list)); 76 if(NULL == re) return NULL; 77 //set function pointer 78 re->find_word = word_list_find_word; 79 re->print = word_list_print; 80 re->push_back = word_list_push_back; 81 //init size is zero 82 re->size = 0; 83 return re; 84} 85/** 86 * @brief delete word list 87 * @params words[in,out] pointer to word list 88 */ 89void delete_word_list(word_list* words){ 90 free(words); 91} 92int main(void) { 93 char line[1001]; 94 word_list* words = new_word_list();//単語リスト 95 if(NULL == words) return 1; 96 //半角スペースで区切られて単語(半角英字)が標準入力される(末尾は改行) 97 if(NULL == fgets(line, countof(line), stdin)){ 98 delete_word_list(words); 99 return 1; 100 } 101 for(const char* word = strtok(line, " \n"); NULL != word; word = strtok(NULL, " \n")) { 102 const size_t found_index = words->find_word(words, word); 103 if(words->size == found_index) { 104 //同単語がない場合は追加 105 words->push_back(words, word); 106 } 107 else { 108 //同単語の場合、カウントアップ 109 ++words->list[found_index].count; 110 } 111 } 112 words->print(words); 113 delete_word_list(words); 114}
投稿2017/07/29 06:33
編集2017/07/29 16:24総合スコア5850
0
幾つか前提変更しましたのでご参考のPGです。
scanf("%s")では改行文字はスペース文字と同じで入力の区切りとみなされて、それ自身を取り込むことはできないのでEOFで終了としました。
c
1#include <stdio.h> 2#include <string.h> 3 4int main(void) { 5 char ward[1001]; //単語 字数は 1<=wards<=1000// 6 int i, j; 7 int ct = 1; //入力された単語の数 8 char list_W[1001][1001]={'\0'}; //単語リスト クリア 9 int ct_Same_W[1001]; //同単語カウント 10 //半角スペースで区切られて単語(半角英字)が標準入力される(末尾は改行) 11 // int lp=1; 12 while(scanf("%s", ward)!=EOF) { // 単語入力 13 for (j = 0; j<ct; j++) { 14 if (strcmp(ward,list_W[j])==0) { //同単語の場合、ct_Same_W[j]でカウント 15 ct_Same_W[j] += 1; 16 break; 17 } 18 else if (j == ct - 1) { //同単語がない場合はlist_W[j]へ代入 19 strcpy(list_W[j], ward); 20 ct_Same_W[j] = 1; 21 ct += 1; 22 break; 23 } 24 } 25 // for (i = 0;i <= strlen(ward);i++) { // \nは入力されないのでEOF(ctl+d)としました。 26 // if (ward[i] == '\n') { //改行があった場合、 27 // lp == 0; //ループを離脱 28 // } 29 // } 30 } 31 for (i = 0; i<ct-1 ;i++) { 32 //単語リストとそれぞれの出現回数を表示 33 printf("%s %d\n", list_W[i], ct_Same_W[i]); 34 } 35}
投稿2017/07/29 06:35
総合スコア4070
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
文字列のコピーには、代入ではなくstrcpy
を使います。
list_W[j] = ward;
を、strcpy(list_W[j], ward);
に置き換えてください。
どうしても=
で代入したい場合は、一文字ずつ処理するか、ポインタを用いる必要があります。
...あと、wardじゃなくてwordじゃないですかね。
私も書いてみました。せっかくなので載せます。
純粋なC言語はしばらく触ってませんので、よくない点も多々あると思いますが...
C
1#include <ctype.h> 2#include <stdio.h> 3#include <stdlib.h> 4#include <string.h> 5 6#define BUF_LEN 1000 7#define WORD_LEN 20 8#define WORD_TYPE 500 9 10typedef struct NL_WORD { 11 char word[WORD_LEN+1]; 12 int count = 0; 13} NL_WORD; 14 15char *read_token(char **, char *); 16int find_name(NL_WORD *, int, char *); 17 18int main(void) { 19 char buffer[BUF_LEN+1]; 20 char *buf_ptr = buffer; 21 22 char input_word[WORD_LEN+1]; 23 24 NL_WORD word_info[WORD_TYPE] = {}; 25 int read_word_num = 0; 26 27 int i; 28 29 fgets(buffer, BUF_LEN, stdin); 30 while(read_token(&buf_ptr, input_word) != NULL) { 31 int index; 32 if( (index = find_name(word_info, read_word_num, input_word)) != -1 ) { 33 word_info[index].count++; 34 } 35 else { 36 strcpy(word_info[read_word_num].word, input_word); 37 word_info[read_word_num].count = 1; 38 read_word_num++; 39 } 40 } 41 42 for(i = 0; i < read_word_num; i++) { 43 printf("word: %s\n", word_info[i].word); 44 printf("num: %d\n", word_info[i].count); 45 } 46 47 system("PAUSE"); 48 return EXIT_SUCCESS; 49} 50 51char *read_token(char **buffer, char *dst) { 52 int start, end; 53 for(start = 0; isspace((*buffer)[start]); start++) { 54 if( (*buffer)[start] == '\0' || 55 (*buffer)[start] == '\n' ) return NULL; 56 } 57 *buffer += start; 58 59 for(end = 0; !isspace((*buffer)[end]); end++) { 60 dst[end] = (*buffer)[end]; 61 } 62 dst[end] = '\0'; 63 *buffer += end; 64 65 return *buffer; 66} 67 68int find_name(NL_WORD *word_list, int word_list_len, char *input_word) { 69 int i; 70 for(i = 0; i < word_list_len; i++) { 71 if( !strcmp( word_list[i].word, input_word ) ) return i; 72 } 73 return -1; 74} 75
このコード、yumetodoさんのコードの劣化版みたいになってますね...
上げてから気付きました。
命名というか、変数名の書き方で、次のような分類があります。
名前(例示) | 別名 | 説明 |
---|---|---|
UpperCamelCase | PascalCase | 単語の頭文字を大文字にして繋げる |
lowerCamelCase | camelCase | 単語の頭文字は大文字、でも最初だけ小文字 |
SNAKE_CASE | SNAIL_CASE | 全部大文字、単語の繋ぎはアンダーバー |
snake_case | snail_case | 全部小文字、単語の繋ぎはアンダーバー |
言語によって、どのケースを用いるかが大体決まっています。
名前を見ただけで、『あ、これは関数だ』『変数だ』『マクロだ』と判断できるので重要です。
また、明白でない場合、略語はあまり用いない方が良いかもしれません。
コードが横に長くなりますが、Cは自由構文なので、適宜改行で調整できます。
投稿2017/07/29 04:49
編集2017/07/29 07:54総合スコア35660
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/07/29 04:53
2017/07/29 10:41
2017/07/29 15:32
0
素早い回答、およびプログラムまで書いてくださってありがとうございます。
回答者様方の書いたものに比べたら大したものではありませんが、
ヒントを頼りに自分なりの答えを書いてみました。
不完全ながらもほぼ半日かけてしまいましたがひとまずはスッキリしました。
とても楽しかったです!
学習不足を痛感したのと同時に、違う言語も学習し、使い分ける事が重要にも感じました。
どの回答者様も興味深い回答をしてくださったのですが、変数の宣言の仕方についても教授いただいた方の回答をベストアンサーにしました。
まだ全て読んでいないので、休憩しながらでもにらめっこしたいと思います。
余談ですが、現在大学4年の就活中で、就活を始めた時期にプログラマーへと志望を変え、文系ながらもコツコツ取り組んでおります。
いまだに内定を取れていませんがどうしてもプログラマーになりたく、頑張っていたのですが、
難しいプログラミングの概念に挫折しそうになっていたところ、たまたまこのサイトに辿り着きました。
今日は本当に質問してよかったと感じています。
また、わからないことがあったら質問させていただきたいと思います。
本当にありがとうございました。
投稿2017/07/29 11:08
総合スコア9
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/07/29 15:29
2017/07/29 17:25 編集
2017/07/29 17:37
2017/07/30 10:07
2017/07/31 02:40
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/07/29 10:44