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

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

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

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

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

3回答

4980閲覧

[C言語] strtok によって切り取られた文字列の文字数カウント

Sophian

総合スコア36

C

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

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2016/05/15 06:47

###前提・実現したいこと
お世話になります。
英文の入力をstrtok によって切り出された単語の文字数のカウントを行うプログラムを書いてみたのですが、クラッシュしてしまいます。

例えば

”This is a pen.”

に対して、単語順にカウントしていき、

This 4 is 2 a 1 pen 3

の様にカウントしたいのですが、できません。

切り出された単語に対してstrlen を呼び出す際にクラッシュしているようですが、理由がわかりません。
strlenについて調べてみたところ、引数に文字配列のポインタをとる関数、ということなので使い方は間違っていないと思うのですが、上手くいきません。

どんなことでも構いませんのでご助言よろしくお願いします。

###入力ファイル
input.dat

In computer science, the dining philosophers problem is an example problem often used in concurrent algorithm design to illustrate synchronization issues and techniques for resolving them. It was originally formulated in 1965 by Edsger Dijkstra as a student exam exercise, presented in terms of computers competing for access to tape drive peripherals. Soon after, Tony Hoare gave the problem its present formulation.

###ソースコード

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#define N 256 //一行の最大長,255文字以下と想定 5 6int main(void){ 7 8 FILE *fp; 9 char *filename="input.dat"; //入力ファイル名 10 char readline[N]; //1行分の読み込みを代入 11 char *word; //strtokによって切り取られた1単語分 12 13 //ファイルが開けない場合の処理 14 if((fp=fopen(filename,"r"))==NULL){ 15 fprintf(stderr,"%s is not available.",filename); 16 exit(EXIT_FAILURE); 17 } 18 19 while((fgets(readline,N,fp))!=NULL){ //1行ずつreadlineへ読み込む 20 21 //読み込んだ1行を空白で区切っていく 22 word=strtok(readline," "); //1回目のstrtok 23 puts(word); 24 25 while(word!=NULL){ 26 word=strtok(NULL," "); //2回目のstrtok 27 puts(word); 28 printf("%d\n",strlen(word)); //文字数カウント, この行がクラッシュの原因 29 } 30 } 31 32 return 0; 33}

問題の1行を以下のコードに書き換え、終端文字'\0'までカウントする事も試してみましたが、同様にクラッシュしてしまいました。

C

1for(i=0;*(word+i)!='\0';i++){ 2} 3printf("%d",i);

###原因となる行を削除した場合の出力結果
単語ごとに切り出されるところまでは順調のようです。

In computer science, the dining philosophers problem is an example problem often used in concurrent algorithm design to illustrate synchronization issues and techniques for resolving them. It was originally formulated in 1965 by Edsger Dijkstra as a student exam exercise, presented in terms of computers competing for access to tape drive peripherals. Soon after, Tony Hoare gave the problem its present formulation.

###補足情報
MinGW
Eclipse Cpp Mars
Windows 10

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

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

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

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

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

HogeAnimalLover

2016/05/15 07:05

クラッシュとは具体的にどういうこと?途中までプログラムは動くの?
Sophian

2016/05/15 07:21

失礼しました。明記すべきでした。実行直後に動作を終了してしまいます。Windowsの窓がでてきて終了するかたちになります。
guest

回答3

0

もしも一行目までは正常に動くならば、行末処理がおかしいと思います。

strlenはNULLが引数である場合、エラーを起こす可能性があると聞いたことがあります。strtokは検索対象を未発見の場合NULLを返しますが、ループ脱出の直前にstrlen(NULL)が発生します。

投稿2016/05/15 07:28

HogeAnimalLover

総合スコア4830

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

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

Sophian

2016/05/15 07:34

詳しく説明していただきありがとうございます! 非常にすっきりしました。最後のstrtokでヌルポインタが返されていることにもっと注意を払うべきでした。以後気を付けたいと思います!
guest

0

ベストアンサー

word=strtok(NULL," "); //2回目のstrtok

strtok()NULLを返しているのに、それに対しstrlen()を呼んでいるのが落ちる原因です。

投稿2016/05/15 07:19

sharow

総合スコア1149

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

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

Sophian

2016/05/15 07:26

解答ありがとうございます! 早速修正し、 if(word!=NULL){  printf("%d\n",strlen(word)); } としたところ無事実行されました!ありがとうございます。
rubato6809

2016/05/15 08:01

その if 文だけで終わりですか?いかにも対症療法な感じですね。 しかも今だに、puts(word); のところでputs()に NULL を渡している(これもクラッシュの原因になりうる)事と、一回目の strtok() が返した "In" の文字数を表示しなくて良いのか?という疑問が残ります。 ともかく、NULLが返る所ではチェックしてから先へ進む事が原則です。 さらに、ループの順序を組み変えれば、NULLの判定は一箇所で済み、スッキリしますよ。 word = strtok(readline, " "); /* 一回目 */ while (word != NULL) { puts(word); printf("%d\n", strlen(... word = strtok(NULL, " "); /* 二回目 */ }
Sophian

2016/05/15 12:00

コード例、補足ありがとうございます! 「NULLが返る所ではチェックしてから先へ進む」 肝に銘じておきたいと思います。 単純なカエサル暗号を解読するプログラムを作っていたところ、プログラムがクラッシュし、原因を探しているうちにstrlenにたどり着きました。当初のコードをそのまま質問で使うのは分かりずらいと思いましたので、とにかく不要な部分を切り落とし、デバッグのためにputsを置いていったところ、このようになってしまいました。 絶対に文末で出現しない(ピリオドコンマがくっつかない)、且出現頻度が高い語である冠詞の”a”と”the”(アスキーで文字間の差が先頭から12,3)を検出するために単語の語数が必要でした。 今回、出力形式は重要でないことを明記すべきでした。 ループの順序も意識していきたいと思います。元のカエサルの方でも似た様な、くどいコードが見つかりました。 加えて、puts()にはNULLを渡さないように注意したいと思います!ありがとうございました。
guest

0

こんにちは。

入力したテキストが1行255文字を超えてしまい、NULL終端していないからでは?


【追記】
しまった。
スマホから見たので改行がないように見えただけでした。
今出先なので後程PCでみてみます。
ごめんなさい。

投稿2016/05/15 07:07

編集2016/05/15 07:15
Chironian

総合スコア23272

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

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

Sophian

2016/05/15 07:19

すみません!わざわざありがとうございます。
Sophian

2016/05/15 07:34

解決しました!ありがとうございました!
Chironian

2016/05/15 08:16

今、戻りました。既に本題は解決してますが、折角なのでちょっとだけやってみました。 少し蛇足を。 > char *filename="input.dat"; MinGW(gcc)の場合、下記警告がでました。 warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings] これは"input.dat"がリテラルなので変更してはいけないけど、char *でポイントすると変更できるから危険だよという警告です。 下記のように変更すれば警告は消えます。 > char const *filename="input.dat"; 実際には最近のOSはセキュリティ対策で書き換え禁止エリアを書き換えようとすると不正アクセスで落ちるので問題がでることはあまりないです。そのようなOSではない場合、間違って変更すると変更できてしまうので、やらかすとハマることがあったりします。なかなか痛いです...orz 後、改行文字も1文字と数えてしまうようです。strtokに改行文字(\n)も追加で指定すれば改行文字を捨てることができます。
Sophian

2016/05/15 11:31

わざわざありがとうございます!承知しました。私の場合ミスがかなり多いので、そういった事前の危機回避は非常に助かります。ファイル名の定義にはconst指定していきたいと思います。 改行について、カウントされているのに気が付きませんでした…。複数指定できることも知りませんでした…。勉強になりました、ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問