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

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

ただいまの
回答率

87.94%

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

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 3,134

score 36

前提・実現したいこと

お世話になります。
英文の入力を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.

ソースコード

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 256 //一行の最大長,255文字以下と想定

int main(void){

    FILE *fp;
    char *filename="input.dat"; //入力ファイル名
    char readline[N]; //1行分の読み込みを代入
    char *word;    //strtokによって切り取られた1単語分

    //ファイルが開けない場合の処理
    if((fp=fopen(filename,"r"))==NULL){
        fprintf(stderr,"%s is not available.",filename);
        exit(EXIT_FAILURE);
    }

    while((fgets(readline,N,fp))!=NULL){    //1行ずつreadlineへ読み込む

        //読み込んだ1行を空白で区切っていく
        word=strtok(readline," ");    //1回目のstrtok
        puts(word);

        while(word!=NULL){
            word=strtok(NULL," ");    //2回目のstrtok
            puts(word);
            printf("%d\n",strlen(word));    //文字数カウント, この行がクラッシュの原因
        }
    }

    return 0;
}


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

for(i=0;*(word+i)!='\0';i++){
}
printf("%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

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • HogeAnimalLover

    2016/05/15 16:05

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

    キャンセル

  • Sophian

    2016/05/15 16:21

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

    キャンセル

回答 3

checkベストアンサー

+1

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


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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/05/15 16:26

    解答ありがとうございます!
    早速修正し、

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

    キャンセル

  • 2016/05/15 17: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, " "); /* 二回目 */
    }

    キャンセル

  • 2016/05/15 21:00

    コード例、補足ありがとうございます!
    「NULLが返る所ではチェックしてから先へ進む」
    肝に銘じておきたいと思います。

    単純なカエサル暗号を解読するプログラムを作っていたところ、プログラムがクラッシュし、原因を探しているうちにstrlenにたどり着きました。当初のコードをそのまま質問で使うのは分かりずらいと思いましたので、とにかく不要な部分を切り落とし、デバッグのためにputsを置いていったところ、このようになってしまいました。
    絶対に文末で出現しない(ピリオドコンマがくっつかない)、且出現頻度が高い語である冠詞の”a”と”the”(アスキーで文字間の差が先頭から12,3)を検出するために単語の語数が必要でした。
    今回、出力形式は重要でないことを明記すべきでした。

    ループの順序も意識していきたいと思います。元のカエサルの方でも似た様な、くどいコードが見つかりました。

    加えて、puts()にはNULLを渡さないように注意したいと思います!ありがとうございました。

    キャンセル

+1

こんにちは。

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


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

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/05/15 16:19

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

    キャンセル

  • 2016/05/15 16:34

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

    キャンセル

  • 2016/05/15 17: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)も追加で指定すれば改行文字を捨てることができます。

    キャンセル

  • 2016/05/15 20:31

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

    キャンセル

+1

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

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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/05/15 16:29

    あ、解決済みになってた。

    キャンセル

  • 2016/05/15 16:34

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

    キャンセル

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

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

関連した質問

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