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

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

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

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

Q&A

1回答

3010閲覧

ファイル中の文字を読み込み、出現回数をカウントするプログラム例について

termae

総合スコア4

C

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

1グッド

2クリップ

投稿2019/10/19 06:28

編集2019/10/19 07:10

前提・実現したいこと

C言語で、二つ以上のファイル(英字だけのテキストファイル)から文字列を読み込み、出現回数をカウントして、出現回数が多い順に並べ替えて出力するプログラムを線形リスト構造を用いて書け、という問題のコードの例を教えてほしいです。。

例えば、
file1.txt

abc
def
abc

file2.txt

abc
hil
def

ならば、実行して

abc 3
def 2
hil 1

と出力されるようなプログラムを作りたいです。
初心者なので助けてください!

■■な機能を実装中に以下のエラーメッセージが発生しました。

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

エラーメッセージ

該当のソースコード

C

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

struct cell{
struct cell *next;
};

int main(int argc, char *argv[])
{
FILE *fp;
int i,chr;

for(i=1;i<argc;i++){
fp=fopen(argv[i], "r");
if(fp==NULL){
fprintf(stderr, "cannot open %s\n", argv[i]);
exit(1);
}

試したこと

ここに問題に対して試したことを記載してください。

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

ここにより詳細な情報を記載してください。

退会済みユーザー👍を押しています

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

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

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

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

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

Zuishin

2019/10/19 06:30

自分で作りたいのであって、答えを教えて欲しいわけではないんですね?
dodox86

2019/10/19 06:50

初心者さんにとってはまあまあテーマが多そうな課題ですね。(ポインタの扱い必須、ファイル入出力、文字列トークン分割、キー/値マップ、線形リスト、ソート等?) ですが、今のままですとさすがに丸投げ扱いで、回答がつくのは難しいと思いますよ。
termae

2019/10/19 07:05

答えを教えてほしいです。。 まだまだできないことが多いので、教えてもらった後に分析しようと思っています。。。
termae

2019/10/19 07:12

アドバイスありがとうございます。 丸投げは良くないのは分かっているのですが、何をどうして良いか全然分かららないのです、、、
Zuishin

2019/10/19 07:13

解き方なら教えられますが、答えは教えられません。そのようなサイトでもありません。 https://teratail.com/help/avoid-asking > 何かを作りたいのでコードを書いてほしい、学校の課題を解いてほしい等の質問は、具体的にプログラミングで困っている質問ではないと考え、推奨していません。
cateye

2019/10/19 07:31 編集

ここ(telatail)で、“文字数カウント”で検索すると100以上回答が見れますが・・・見ましたか? ・・・どうも皆さん、質問をするのは良いのですが、過去の事例を確認することが苦手のようで・・・
guest

回答1

0

Cの初心者がやるような課題とは思えません。6時間ぐらいかかりました。

コードだけ欲しいようですので下記にあげますが、著作権は放棄していません。Copyrightとその下の条項を決して削除しないでください。

C

1/** 2 * 単語カウントアッププログラム v1.0 2019-10-19 3 * 4 * このプログラムはteratailの下記質問の回答のために作成された物です。 5 * https://teratail.com/questions/218076 6 * 7 * 質問の内容では明記されていない部分について、下記の仕様としています。 8 * 9 * - 空白切りの単語をカウントします。空白文字は[ \t\r\n\f\v]とします。 10 * - メモリの許す限り長い文字列も扱います。 11 * ただし、`SIZE_MAX - BUFF_SIZE_INC`を越えることはできませんが、 12 * 64bit環境ではそれだけのメモリを用意することは現実的ではありません。 13 * - null文字が含まれる場合、エラーで終了します。 14 * "テキスト"にはnull文字が含まれていないからです。 15 * - テキストの文字コードは考慮しませんが、エラーメッセージはUTF-8固定で 16 * あるため、UTF-8環境で実行する必要があります。 17 * そのため、テキストファイルの文字コードもUTF-8でなければ、 18 * 文字化けを起こす可能性があります。 19 * - 次期バージョンでは既存文字検索にハッシュテーブルを使用して、 20 * 爆速になる予定です。 21 * - 次期バージョンの開発予定は未定です。 22 * 23 * このプログラムのラインセンスはGPLv3です。 24 * プログラムをコピーする際はライセンスの遵守をお願いします。 25 * 26 * --- 27 * Copyright (c) 2019 raccy 28 * GNU General Public License 29 * 30 * This program is free software: you can redistribute it and/or modify 31 * it under the terms of the GNU General Public License as published by 32 * the Free Software Foundation, either version 3 of the License, or 33 * (at your option) any later version. 34 * 35 * This program is distributed in the hope that it will be useful, 36 * but WITHOUT ANY WARRANTY; without even the implied warranty of 37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 38 * GNU General Public License for more details. 39 * 40 * You should have received a copy of the GNU General Public License 41 * along with this program. If not, see <http://www.gnu.org/licenses/>. 42 */ 43 44#include <errno.h> 45#include <stdbool.h> 46#include <stdint.h> 47#include <stdio.h> 48#include <stdlib.h> 49#include <stdnoreturn.h> 50#include <string.h> 51 52#define BUFF_SIZE_INC 256 53 54struct count_list; 55struct str_list; 56 57struct count_list { 58 uint32_t count; 59 struct count_list *next; 60 struct count_list *prev; 61 struct str_list *slp; 62}; 63 64struct str_list { 65 uint32_t hash; 66 size_t len; 67 struct str_list *next; 68 struct str_list *prev; 69 struct count_list *clp; 70 char *str; 71}; 72 73static inline noreturn void die(const char *message) 74{ 75 perror(message); 76 exit(1); 77} 78 79struct str_list *create_slp_from_stream(FILE *stream); 80void free_slp(struct str_list *slp); 81void free_clp(struct count_list *clp); 82void add_slp_to_clp(struct str_list *slp, struct count_list *clp); 83void remove_slp_from_clp(struct str_list *slp); 84void add_or_countup_slp_to_first_clp(struct str_list *slp, 85 struct count_list *first_clp); 86struct count_list *get_or_create_next_clp(struct count_list *clp); 87void countup_slp(struct str_list *slp); 88struct str_list *search_slp_in_clp(struct str_list *slp, 89 struct count_list *clp); 90 91int main(int argc, char *argv[]) 92{ 93 struct count_list *first_clp = 94 (struct count_list *)malloc(sizeof(struct count_list)); 95 if (first_clp == NULL) die(u8"メモリ取得に失敗しました。"); 96 first_clp->count = 0; 97 first_clp->next = first_clp; 98 first_clp->prev = first_clp; 99 first_clp->slp = NULL; 100 101 for (int i = 1; i < argc; i++) { 102 FILE *fp = fopen(argv[i], "r"); 103 if (fp == NULL) { 104 perror(u8"ファイルを開けませんでした。"); 105 continue; 106 } 107 struct str_list *slp; 108 while ((slp = create_slp_from_stream(fp)) != NULL) { 109 add_or_countup_slp_to_first_clp(slp, first_clp); 110 } 111 } 112 113 for (struct count_list *curr_clp = first_clp->prev; 114 curr_clp != first_clp; curr_clp = curr_clp->prev) { 115 if (curr_clp->slp == NULL) continue; 116 struct str_list *curr_slp = curr_clp->slp; 117 do { 118 printf("%s %d\n", curr_slp->str, curr_clp->count); 119 curr_slp = curr_slp->next; 120 } while (curr_slp != curr_clp->slp); 121 } 122 123 free_clp(first_clp); 124 return 0; 125} 126 127struct str_list *create_slp_from_stream(FILE *stream) 128{ 129 int c = getc(stream); 130 switch (c) { 131 case EOF: 132 return NULL; 133 case ' ': 134 case '\t': 135 case '\r': 136 case '\n': 137 case '\f': 138 case '\v': 139 return create_slp_from_stream(stream); 140 } 141 ungetc(c, stream); 142 143 struct str_list *slp = 144 (struct str_list *)malloc(sizeof(struct str_list)); 145 if (slp == NULL) die(u8"メモリ取得に失敗しました。"); 146 slp->hash = 0; 147 slp->len = 0; 148 slp->next = NULL; 149 slp->prev = NULL; 150 slp->clp = NULL; 151 slp->str = NULL; 152 153 size_t buff_size = 0; 154 bool line_end = false; 155 while (!line_end) { 156 157 if (buff_size <= slp->len) { 158 if (buff_size > SIZE_MAX - BUFF_SIZE_INC) 159 die(u8"サイズの限界を超えています"); 160 buff_size = buff_size + BUFF_SIZE_INC; 161 slp->str = (char *)realloc(slp->str, buff_size); 162 if (slp->str == NULL) 163 die(u8"メモリ取得に失敗しました。"); 164 } 165 int c = getc(stream); 166 switch (c) { 167 case EOF: 168 case ' ': 169 case '\t': 170 case '\r': 171 case '\n': 172 case '\f': 173 case '\v': 174 line_end = true; 175 break; 176 case '\0': 177 die(u8"null文字 '\0' " 178 u8"が含まれたファイルは読み込みません。"); 179 break; 180 default: 181 slp->str[slp->len] = c; 182 slp->hash ^= (uint32_t)c << (slp->len % 4) * 8; 183 slp->len++; 184 break; 185 } 186 } 187 if (slp->len != 0) { 188 slp->str[slp->len] = '\0'; 189 190 return slp; 191 } else { 192 free_slp(slp); 193 return create_slp_from_stream(stream); 194 } 195} 196 197void free_slp(struct str_list *slp) 198{ 199 free(slp->str); 200 free(slp); 201} 202 203void free_clp(struct count_list *clp) 204{ 205 if (clp->next->count != 0) free_clp(clp->next); 206 if (clp->slp != NULL) { 207 struct str_list *curr_slp = clp->slp; 208 do { 209 struct str_list *next_slp = curr_slp->next; 210 free_slp(curr_slp); 211 curr_slp = next_slp; 212 } while (curr_slp != clp->slp); 213 } 214 free(clp); 215} 216 217void add_slp_to_clp(struct str_list *slp, struct count_list *clp) 218{ 219 220 if (clp->slp == NULL) { 221 clp->slp = slp; 222 slp->next = slp; 223 slp->prev = slp; 224 } else { 225 struct str_list *last_slp = clp->slp->prev; 226 slp->next = clp->slp; 227 slp->prev = last_slp; 228 last_slp->next = slp; 229 clp->slp->prev = slp; 230 } 231 slp->clp = clp; 232} 233 234void remove_slp_from_clp(struct str_list *slp) 235{ 236 struct count_list *clp = slp->clp; 237 struct str_list *next_slp = slp->next; 238 struct str_list *prev_slp = slp->prev; 239 if (next_slp == slp) { 240 next_slp = NULL; 241 } else { 242 next_slp->prev = prev_slp; 243 prev_slp->next = next_slp; 244 } 245 if (clp->slp == slp) clp->slp = next_slp; 246 247 slp->next = NULL; 248 slp->prev = NULL; 249 slp->clp = NULL; 250} 251 252void add_or_countup_slp_to_first_clp(struct str_list *slp, 253 struct count_list *first_clp) 254{ 255 struct str_list *found_slp = search_slp_in_clp(slp, first_clp); 256 if (found_slp == NULL) { 257 add_slp_to_clp(slp, get_or_create_next_clp(first_clp)); 258 } else { 259 countup_slp(found_slp); 260 free_slp(slp); 261 } 262} 263 264struct count_list *get_or_create_next_clp(struct count_list *clp) 265{ 266 if (clp->next->count == 0) { 267 if (clp->count >= UINT32_MAX) 268 die(u8"数え上げる個数の限界を超えました。"); 269 struct count_list *next_clp = 270 (struct count_list *)malloc(sizeof(struct count_list)); 271 if (next_clp == NULL) die(u8"メモリ取得に失敗しました。"); 272 next_clp->count = clp->count + 1; 273 next_clp->slp = NULL; 274 275 next_clp->next = clp->next; 276 next_clp->prev = clp; 277 clp->next = next_clp; 278 next_clp->next->prev = next_clp; 279 } 280 return clp->next; 281} 282 283void countup_slp(struct str_list *slp) 284{ 285 struct count_list *next_clp = get_or_create_next_clp(slp->clp); 286 remove_slp_from_clp(slp); 287 add_slp_to_clp(slp, next_clp); 288} 289 290struct str_list *search_slp_in_clp(struct str_list *slp, struct count_list *clp) 291{ 292 if (clp->slp != NULL) { 293 struct str_list *curr_slp = clp->slp; 294 do { 295 if (slp->hash == curr_slp->hash && 296 strcmp(slp->str, curr_slp->str) == 0) { 297 return curr_slp; 298 } 299 curr_slp = curr_slp->next; 300 } while (curr_slp != clp->slp); 301 } 302 if (clp->next->count == 0) return NULL; 303 return search_slp_in_clp(slp, clp->next); 304}

プログラムを書くだけで疲れたので、解説はありません。私の計算が間違ってなければ、検索がハッシュテーブルじゃ無いので、O(n + m^2)(nは総文字数、mは見つかる単語数)ぐらいだと思います。ハッシュテーブルを採用したら、O(n)にできると思われます。

手元では動きましたが、テストはそんなにしていないので、バグはたくさん残っていると思います。もし、見つかったら、テストケースを含めてご指摘下さい。

投稿2019/10/19 13:15

編集2019/10/19 13:18
raccy

総合スコア21735

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

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

termae

2019/10/19 13:59

ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問