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

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

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

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

Q&A

解決済

2回答

5675閲覧

C言語 線形リストで辞書順でのソートが上手く行えない。

Malo

総合スコア19

C

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

0グッド

0クリップ

投稿2018/07/01 15:48

編集2018/07/01 17:01

前提・実現したいこと

C言語, teratail初心者です。
学校の課題で「テキストファイルから単語を読み取り、同じ単語が出現すれば回数を記録し、単語は辞書順にソートして線形リストを作る。」という課題です。このソートがうまくいきません。

初心者故にコードが汚い所があると思いますが、ご助力いただけると幸いです。
解決策でなくても、「ここが駄目」っていうポイントだけでもありがたいです。

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

大体の単語はソート出来ているのですが、一部上手くソートされていません。
(Mrsという単語が、辞書順で最後尾の訳では無いのに、最後尾に行ってしまう
a行が上手くソートされない など。)

該当のソースコード

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5#define True 1 6#define False 0 7 8typedef struct word{ 9 char name[20]; 10 int count; 11 struct word *next; 12}Word; 13 14/* ---プロトタイプ宣言--- */ 15int read_word(FILE*, char[]); 16void print_nodes(Word*, FILE*); 17Word *add_node(char[], Word*); 18Word *create_word(char[]); 19int check_word(char[], Word*); 20Word *sort_node(char[], Word*); 21 22/* ---main関数--- */ 23int main(void){ 24 FILE *fp; 25 FILE *fp_print; 26 Word *root = NULL; 27 char buf[20]; 28 29 fp = fopen("anne_short.txt", "r"); 30 fp_print = fopen("output.txt", "a"); 31 32 if(fp == NULL){ 33 printf("Error!\n"); 34 exit(1); 35 }else{ 36 printf("リスト作成中\n"); 37 38 while(read_word(fp, buf) > 0){ 39 root = add_node(buf, root); 40 } 41 42 printf("リスト作製終了\n"); 43 print_nodes(root, fp_print); 44 } 45 fclose(fp); 46 fclose(fp_print); 47 48 return 0; 49} 50 51/* ---anne_short.txtから単語の読み取り--- */ 52int read_word(FILE *fp, char buf[]){ 53 int ch; 54 int index = 0; 55 int i; 56 57 while((ch = fgetc(fp)) != EOF){ 58 switch(ch){ 59 case ' ': 60 case ',' : 61 case '.': 62 case '\n': 63 case '\0': 64 if(index == 0){ //読み飛ばす文字が続いた場合 65 break; 66 }else{ 67 buf[index++] = '\0'; 68 return index; 69 } 70 default: 71 buf[index++] = ch; 72 break; 73 } 74 } 75} 76 77/* ---nodeのnameを出力--- */ 78void print_nodes(Word *out, FILE *fp_print){ 79 if(out != NULL){ 80 printf("単語:%s 回数:%d回\n", out->name, out->count); 81 fprintf(fp_print, "単語:%s 回数:%d回\n", out->name, out->count); 82 print_nodes(out->next, fp_print); 83 } 84} 85 86/* ---新規nodeの追加--- */ 87Word *add_node(char buf[], Word *root){ 88 Word *behind;// 挿入する直前のnodeのアドレス 89 Word *front;// 挿入する直後のnodeのアドレス 90 Word *tmp; 91 92 // 初回実行時はrootに新規nodeを紐づけ 93 if(root == NULL){ 94 root = create_word(buf); 95 96 // 2回目以降 97 }else{ 98 if((check_word(buf, root)) == True){// True = 新出の単語 99 behind = sort_node(buf, root); 100 front = behind->next; 101 102 if(behind == root){// 先頭に追加する場合 103 tmp = create_word(buf); 104 root = tmp; 105 tmp->next = behind; 106 107 }else if(behind->next == NULL){// 最後尾に追加する場合 108 behind->next = create_word(buf); 109 110 }else{// リストの途中に追加する場合 111 tmp = create_word(buf); 112 behind->next = tmp; 113 tmp->next = front; 114 } 115 } 116 } 117 return root; 118} 119 120/* ---新規nodeの作製--- */ 121Word *create_word(char buf[]){ 122 Word *node = (Word*)malloc(sizeof(Word)); 123 124 // 新規nodeの初期化 125 node->next = NULL; 126 node->count = 1; 127 strcpy(node->name, buf); 128 129 return node; 130} 131 132/* ---新出単語かどうかのチェック--- */ 133int check_word(char buf[], Word *root){ 134 Word *checker = root; 135 136 // 新出ならTrue, 既出ならcountをインクリメントしてFalseを返す 137 while(checker != NULL){ 138 if(stricmp(checker->name, buf) == 0){ 139 checker->count++; 140 return False; 141 }else{ 142 checker = checker->next; 143 } 144 } 145 return True; 146} 147 148/* ---nodeを挿入する際に辞書順に--- */ 149Word *sort_node(char buf[], Word *root){ 150 Word *node = root; 151 Word *behind = node; // 単語を挿入する直前のnodeのアドレス 152 153 while(stricmp(buf, node->name) > 0){ 154 if(node->next != NULL){ 155 behind = node; 156 node = node->next; 157 }else{ 158 break; 159 } 160 } 161 162 return behind; 163} 164

試したこと

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

私の環境を記させて頂きます。足りない情報がございましたら教えていただけると幸いです。
Windows 8
テキストエディタ : サクラエディタ
コンパイラ mingw-w64

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

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

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

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

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

guest

回答2

0

ベストアンサー

割とよく出来てるけど、惜しいなーと思いました。

read_wordEOF に到達したときの返り値

while ループから出た箇所に return index; が必要です。

sort_node の返り値で判断できないケースが有る

じっくり処理を追ってみるとわかると思うのですが、 root のみノードが存在して末尾の要素を追加する場合を考えてみましょう。

c

1/* ---nodeを挿入する際に辞書順に--- */ 2Word *sort_node(char buf[], Word *root){ 3 Word *node = root; 4 Word *behind = node; // 単語を挿入する直前のnodeのアドレス 5 6 while(stricmp(buf, node->name) > 0){ 7 if(node->next != NULL){ 8 behind = node; 9 node = node->next; 10 }else{ 11 break; 12 } 13 } 14 15 return behind; 16}

stricmp(buf, node->name) > 0 かつ node->next == NULL なのでループが終了し、結果として behindroot と一致します。
したがって末尾に追加すべき( behind->next == NULL )か先頭に追加すべき( behind == root )か判断がつかない状態になってしまいます。

解決策としては、 behind の初期値を NULL にして先頭に挿入すべきケースを明確に分ける方法ですね。

c

1/* ---nodeを挿入する際に辞書順に--- */ 2Word *sort_node(char buf[], Word *root){ 3 Word *node = root; 4 Word *behind = NULL; // 単語を挿入する直前のnodeのアドレス 5 6 while(stricmp(buf, node->name) > 0){ 7 behind = node; // 挿入すべき位置は node の位置より後 8 if(node->next != NULL){ 9 node = node->next; 10 }else{ 11 break; 12 } 13 } 14 15 return behind; 16}

これによって他の箇所も変更する必要があると思うので、よしなに。

投稿2018/07/02 01:26

mather

総合スコア6753

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

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

Malo

2018/07/02 17:54

回答ありがとうございました! 適切なアドバイス、自分で考えさせる余地が程よくあり、とても勉強になりました!
guest

0

回答ではないですが、
Windowsを使っているなら、VisualStudioを使ってデバッグしてみてはどうでしょう
ソースコード上の任意の行にブレークポイントを設置し、実行をそこで止め、各変数の内容を参照することができます。
また、1行づつ実行し、変数の内容をモニタするようなこともできます

投稿2018/07/01 22:53

y_waiwai

総合スコア87749

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

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

y_waiwai

2018/07/03 00:40

しかし、Teratailのポイントシステムは単なる人気投票でしかないというのはわかってるつもりだけど、デバッグの方法を提示したらマイナスつくというのは驚愕やな。 なにをしたいんだろう
YouheiSakurai

2018/07/03 03:28

デバッグ方法ではなくデバッグすれば出来ることを記載したからでしょう。VisualStudioでCにブレイクポイントを設定してデバッグしたことあります?私はないですし、多分出来るようになるまで少なくとも数日かかると思います。そんな人、もしくはこれをやった事がある人は、この回答をゴールへの道のりの長さに反して薄いと感じてるんじゃないかと思います。(私は感じてます。)
y_waiwai

2018/07/03 03:37

デバッグできないと思われるような初心者だからデバッグ方法を教えちゃダメって話? なんじゃそれ 質問くんにはもう進歩するなと言ってるのと一緒じゃないの? Cのコードでブレークポイント設定してデバッグしないってことは、ひたすらコードとにらめっこ? 世の中というのはオレが思ってるほど進んでないのかな?
YouheiSakurai

2018/07/03 03:45

そういう事、世の中との乖離だと思います。
kazto

2018/07/03 12:21

横から失礼します。 さすがにデバッグを知らずにプログラミングするのは非効率極まると思います。 実際にプログラムが動く様子を観察して理解が深まることも多々あります。 y_waiwaiさんのご意見が世の中と乖離しているとは、思いません。
YouheiSakurai

2018/07/03 12:41 編集

まず、VisualStudioってみんなが使っているデファクトスタンダードの製品か、否、シェアは高めだがそうではないと感じています。次に、人はいつデバッガを使えるようになるものなのか、私はある程度プログラミングをやってて必要に迫られた時だと思っています。最後に、VisualStudioってそんなにとっつきやすいのか、私はMSの流儀を知らないと難しいんじゃないかと想像しています。何もデバッガの有用性を否定しているわけではありません。世の中はそんな感じで、特に職業プログラマではない学生に対して「Windowsを使っているなら、VisualStudioを使ってデバッグしてみてはどうでしょう」の一言で片付かないと主張しているまでです。
pepperleaf

2018/07/03 13:08 編集

デバッグそのものは否定しませんが、環境に mingw-w64 とあり、無視してませんか? また、デバッガを使うにもエネルギーは必要だし、最初から、使えたら、苦労しない。 そして、回答そのものが、"努力が足りない、もっと頑張れ" みたいな感じで、全く回答になって無いと感じました。無い方がマシです。他の方に任せるべきです。
can110

2018/07/03 13:09

質問者様の環境(サクラエディタ/mingw-w64)から全くの初心者とは思えません。 VSも比較的簡単に使いこなせると感じてます。ネットでの情報も豊富ですし。 また、質問コードの規模からもデバッガを使うのに良いタイミングでしょう。 よってVSでのデバッグを勧めるのは自然なことだと思います。 ま、シェアとか流儀とはおいといて、まずは使ってみて判断してもよいかと。
pepperleaf

2018/07/03 13:18

can110さん、 > 質問者様の環境(サクラエディタ/mingw-w64) サクラエディタは、あちこちで良く聞きます。初心者に薦めるところも多いようです。mingw-w-64 も gccのようなので、フリーのコンパイラとして使われる事が多いと思われます。(インストール済なら、 gcc xxx.c だけで使える) そして、最初に"学校で..." とある事から、先生(or 学校)の推奨または、指定である可能性が高いです。VSが使えるか? と言うより、学校のPCに入れられるか? もっと大きな問題は、それが質問者の期待する回答かと言う事ですね。 そういう意味では、ここで本来の質問と離れた議論をする事自体が大変、失礼なのですが。
YouheiSakurai

2018/07/03 13:22

ここまでをまとめると私は主張が割れやすい箇所を突いているという事ですね。まぁ正解のない話なのでそれが分かれば私的にはOKです。
can110

2018/07/03 13:33

pepperleafさん、コメントありがとうございます。 学校指定のPCだとすでに環境は構築済みの可能性はありますね。見落としてました。 ただ「回答ではないですが」と断られてたうえでの回答でありますし 「デバッガ使うといいよ」という意見は基本的に正しいと思いましたので、それに賛同するためにコメントしました。 たしかに本質問には関係ないですね。失礼しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問