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

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

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

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

Q&A

3回答

877閲覧

strtokの使い方について

KZK13

総合スコア43

C

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

0グッド

0クリップ

投稿2020/08/05 16:02

編集2020/08/05 16:04
n = 0; words[n] = strtok(buf, "\t\n"); while (n < 100 && words[n]) { strtok(NULL, "\t\n"); words[++n] = strtok(NULL, "\t\n"); }

上のプログラムにおいて、

n = 0; words[n] = strtok(buf, "\t\n"); while (n < 100 && words[n]) { strtok(NULL, "\t"); words[++n] = strtok(NULL, "\t\n"); }

とした時に「明日は晴れ」と入力した際に「は」「晴れ」がNULLになる理由が知りたいです。
こちらは全体をコードです。
https://pastebin.com/DbJQLwGM

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

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

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

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

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

otn

2020/08/05 23:02

> 「は」「晴れ」がNULLになる というのは具体的にどういう現象のことを言ってますか?
rubato6809

2020/08/06 01:14

質問にあげたようなコード断片でなく、 「こちらは全体をコード」でもなく、 <「明日は晴れ」と入力した際に「は」「晴れ」がNULLになる>ことを誰でも確認できる、できるだけ小さなテストプログラムを作り、質問に貼り付けたらいかが? 全体コードのうち、その現象に関係ない部分をどんどん削っていけば、そのテストプログラムになるはずだよね。
episteme

2020/08/06 01:52

MeCabもDxLibも使わずに十分短い再現可能コードを呈示できるはずなんだが...
fana

2020/08/06 01:55

(ここまで停滞するくらいなら,strtokなんてつまらん物は忘れて,自前で分割すりゃいいのではないかと.)
episteme

2020/08/06 02:02

テキトーに決めた区切り文字で分割するだけやからね。
Zuishin

2020/08/06 02:13

前の質問でもその前の質問でも strtok は不要と言われてるんですが、いろんなものを切り貼りした結果、自分でも全貌がつかめなくなっているので、strtok を切り離すとどうなるのか彼にとってまったく未知の世界になっています。
Zuishin

2020/08/06 02:16

carnage0216 さんに教えるべきは、彼の望みではあるが彼に理解できない優れたコードではなく、まずモジュール化とステップ実行と単体テストだろうと思います。
KZK13

2020/08/06 04:29

え、分解はmecabでやってるけど、単語以外の部分を削るためにあるのがstrtokなんですよね?
Zuishin

2020/08/06 04:32

> 前の質問でもその前の質問でも strtok は不要と言われてるんですが
otn

2020/08/06 04:33

自分で > 「は」「晴れ」がNULLになる と書いてみたものの、書い意味が自分でもわかっておらず、説明できないと言うことですか?
episteme

2020/08/06 04:35 編集

strtokが文字列を"削る"ためのものと思っているなら、リファレンスを読むことをお勧めする。
m.ts10806

2020/08/06 09:56 編集

分からないなら無理して使わなくても良いと思います。分からないものを使おうとしても理解に繋がりませんし(プログラミング自体)。 理解したいならドキュメント読んで自分で考えて自分でコードを組むしかありません(プログラミングの基本)。 他人の説明が受け入れられない性質をなんとかできないようですから、他人を頼っていては使えるようになりません(コミュニケーションの基本)。
guest

回答3

0

KZK13さん、あなたは質問がメチャクチャ下手です。
次のように質問すれば誤解が無いと思いませんか?

C

1#include <stdio.h> 2#include <string.h> 3 4int main() 5{ 6 char buf[] = "明日\t名詞,アシタ\nは\t助詞,ワ\n晴れ\t名詞,ハレ\n"; 7 char *words[100]; 8 int n = 0; 9 words[n] = strtok(buf, "\t\n"); 10 while (n < 100 && words[n]) { 11 strtok(NULL, "\t\n"); 12 words[++n] = strtok(NULL, "\t\n"); 13 } 14 for (int i = 0; i < n; i++) { 15 if (words[i] == NULL) 16 printf("words[%d] = NULL\n", i); 17 else 18 printf("words[%d] = [%s]\n", i, words[i]); 19 } 20}

実行結果

plain

1words[0] = [明日] 2words[1] = [は] 3words[2] = [晴れ]

上のコードの strtok(NULL, "\t\n");"\t\n""\t" に変えると
実行結果が次のように変わりました。

plain

1words[0] = [明日] 2words[1] = [助詞,ワ] 3words[2] = [名詞,ハレ]

「は」と「晴れ」が表示されなくなった理由が知りたいです。

追記
char buf[] = "明日\t名詞,アシタ\nは\t助詞,ワ\n晴れ\t名詞,ハレ\n";
で、区切り文字が "\t\n" だけの場合のコードを説明します。

words[n] = strtok(buf, "\t\n"); で strtok は buf の先頭から、'\t' または
'\n' を探しに行き、buf[4] で '\t' を見つけ、そこに '\0' を書き込んで
"明日" という文字列を切り出します。そして、buf[5] のアドレスを内部に保存し、
buf[0] のアドレスを返します。

words[0] には "明日" の先頭アドレスが入ります。
その値は NULL ではないので、whileループの中に入ります。

strtok(NULL, "\t\n"); で、NULL を渡された strtok は内部に保存していた
buf[5] のところから、区切り文字の検索をし、buf[16] で '\n' を見つけます。
buf[16] に '\0' を書き込んで、"名詞,アシタ" という文字列を切り出します。
そして、buf[17]のアドレスを内部に保存し、buf[5]のアドレスを返します。
strtok の呼び出し元では返ってきた値をどこにも代入せず無視します。

words[++n] = strtok(NULL, "\t\n"); で、NULL を渡された strtok は内部に
保存していたbuf[17] のところから、区切り文字の検索をし、buf[19] で '\t' を
見つけ、そこに '\0' を書き込んで "は" という文字列を切り出します。
そして、buf[20]のアドレスを内部に保存し、buf[17]を返します。
strtok の呼び出しもとでは返ってきた値を words[++n] に代入します。
words[1] には "は" の先頭アドレスが入ります。

while の先頭に戻ります。

words[1] の値は NULL ではないので、whileループの中に入ります。

strtok(NULL, "\t\n"); で、NULL を渡された strtok は内部に保存していた
buf[20] のところから、区切り文字の検索をし、buf[27] で '\n' を見つけます。
buf[27] に '\0' を書き込んで、"助詞,ワ" という文字列を切り出します。
そして、buf[28]のアドレスを内部に保存し、buf[20]のアドレスを返します。
strtok の呼び出し元では返ってきた値をどこにも代入せず無視します。

words[++n] = strtok(NULL, "\t\n"); で、NULL を渡された strtok は内部に
保存していたbuf[28] のところから、区切り文字の検索をし、buf[32] で '\t' を
見つけ、そこに '\0' を書き込んで "晴れ" という文字列を切り出します。
そして、buf[33]のアドレスを内部に保存し、buf[28]を返します。
strtok の呼び出し元では返ってきた値を words[++n] に代入します。
words[2] には "晴れ" の先頭アドレスが入ります。

while の先頭に戻ります。

words[2] の値は NULL ではないので、whileループの中に入ります。

strtok(NULL, "\t\n"); で、NULL を渡された strtok は内部に保存していた
buf[33] のところから、区切り文字の検索をし、buf[42] で '\n' を見つけます。
buf[42] に '\0' を書き込んで、"名詞,ハレ" という文字列を切り出します。
そして、buf[43]のアドレスを内部に保存し、buf[33]のアドレスを返します。
strtok の呼び出し元では返ってきた値をどこにも代入せず無視します。

words[++n] = strtok(NULL, "\t\n"); で、NULL を渡された strtok は内部に
保存していたbuf[43] のところから、区切り文字の検索をしますが、buf[43] は
'\0' で文字列の最後です。文字列の切り出しはできず、strtok は NULL を返します。
strtok の呼び出し元では返ってきた値 NULL を words[++n] に代入します。
words[3] には NULL が入ります。

while の先頭に戻ります。
words[3] の値は NULL なので、whileループは終了します。これで
words[0] = "明日", words[1] = "は", words[2] = "晴れ", words[3] = NULL
となりました。


今度は whileループの中の最初の strtok の呼び出しの区切り文字が "\t" の場合です。

words[n] = strtok(buf, "\t\n"); で strtok は buf の先頭から、'\t' または
'\n' を探しに行き、buf[4] で '\t' を見つけ、そこに '\0' を書き込んで
"明日" という文字列を切り出します。そして、buf[5] のアドレスを内部に保存し、
buf[0] のアドレスを返します。

words[0] には "明日" の先頭アドレスが入ります。
その値は NULL ではないので、whileループの中に入ります。

strtok(NULL, "\t"); で、NULL を渡された strtok は内部に保存していた
buf[5] のところから、区切り文字の検索をし、buf[19] で '\t' を見つけます。
buf[19] に '\0' を書き込んで、"名詞,アシタ\nは" という文字列を切り出します。
そして、buf[20]のアドレスを内部に保存し、buf[19]のアドレスを返します。
strtok の呼び出し元では返ってきた値をどこにも代入せず無視します。

words[++n] = strtok(NULL, "\t\n"); で、NULL を渡された strtok は内部に
保存していたbuf[20] のところから、区切り文字の検索をし、buf[27] で '\n' を
見つけ、そこに '\0' を書き込んで "助詞,ワ" という文字列を切り出します。
そして、buf[28]のアドレスを内部に保存し、buf[20]を返します。
strtok の呼び出しもとでは返ってきた値を words[++n] に代入します。
words[1] には "助詞,ワ" の先頭アドレスが入ります。

while の先頭に戻ります。

words[1] の値は NULL ではないので、whileループの中に入ります。

strtok(NULL, "\t"); で、NULL を渡された strtok は内部に保存していた
buf[28] のところから、区切り文字の検索をし、buf[32] で '\t' を見つけます。
buf[32] に '\0' を書き込んで、"晴れ" という文字列を切り出します。
そして、buf[33]のアドレスを内部に保存し、buf[20]のアドレスを返します。
strtok の呼び出し元では返ってきた値をどこにも代入せず無視します。

words[++n] = strtok(NULL, "\t\n"); で、NULL を渡された strtok は内部に
保存していたbuf[33] のところから、区切り文字の検索をし、buf[42] で '\n' を
見つけ、そこに '\0' を書き込んで "名詞,ハレ" という文字列を切り出します。
そして、buf[43]のアドレスを内部に保存し、buf[33]を返します。
strtok の呼び出し元では返ってきた値を words[++n] に代入します。
words[2] には "名詞,ハレ" の先頭アドレスが入ります。

while の先頭に戻ります。

words[2] の値は NULL ではないので、whileループの中に入ります。

strtok(NULL, "\t"); で、NULL を渡された strtok は内部に保存していた
buf[43] のところから、区切り文字の検索をしますが、buf[43] は '\0' なので、
文字列の最後です。文字列の切り出しはできず、strtok は NULL を返します。
strtok の呼び出し元では返ってきた値をどこにも代入せず無視します。

words[++n] = strtok(NULL, "\t\n"); で、NULL を渡された strtok は内部に
保存していたbuf[43] のところから、区切り文字の検索をしますが、buf[43] は '\0'
なので、文字列の最後です。文字列の切り出しはできず、strtok は NULL を返します。
strtok の呼び出し元では返ってきた値 NULL を words[++n] に代入します。
words[3] には NULL が入ります。

while の先頭に戻ります。
words[3] の値は NULL なので、whileループは終了します。これで
words[0] = "明日", words[1] = "助詞,ワ", words[2] = "名詞,ハレ",
words[3] = NULL となりました。


要するに、全部 "\t\n" で区切ると、

"明日"
"名詞,アシタ"
"は"
"助詞,ワ"
"晴れ"
"名詞,ハレ"
NULL

になるものが、"\t\n", "\t", "\t\n" で区切ると

"明日"
"名詞,アシタ\nは"
"助詞,ワ"
"晴れ"
"名詞,ハレ"
NULL
NULL

になるということです。

投稿2020/08/06 06:49

編集2020/08/08 05:45
kazuma-s

総合スコア8224

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

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

DreamTheater

2020/08/06 07:02 編集

すみません読み違えていました。 先ほどのコメントを削除し、上記回答に1票を投じます。
episteme

2020/08/06 09:31 編集

質問に限らずコメント欄での受け答えも要領を得ないんよなー... 「そんなこと訊いてない」や「答えになってない」が少なくない。 想いを言葉にする とか ロジカルに語る ことが苦手なんかな。
rubato6809

2020/08/06 09:53

なるほど〜。strtok()は知ってるけどMeCab門外漢の私にも事情がのみこめました。 つまり質問者は、このデータ構造も、strtok()の働きも、曖昧なままなんですね。
episteme

2020/08/06 10:01

うん、彼はその曖昧さを正そうとしているのだろうけど、それを質問として表現できていない。
fana

2020/08/07 04:05

(このようなことをコードを交えて書ける適切な場所がないのでここに書いたのはわかりますが,本来は質問ではなく回答を書く場所なので,) 「回答」(すなわち,この場合の"理由")も一緒に書けばよろしいのではないかと.
KZK13

2020/08/18 10:52

あの、ちなみに、 strtok(NULL, "\t\n"); words[++n] = strtok(NULL, "\t\n"); の部分を strtok(NULL, "\t"); words[++n] = strtok(NULL, "\t\n"); にした場合はへんてこな出力になりますが、\t\n"で統一したほうがいいということでしょうか?
kazuma-s

2020/08/18 11:11

まず、なぜ、"\t" にしたいのかを詳しく説明してください。 strtok の区切り文字を "\t\n" ではなく、一文字にしたいのなら、 words[n] = strtok(buf, "\t"); while (n < 100 && words[n]) { strtok(NULL, "\n"); words[++n] = strtok(NULL, "\t"); } となることが理解できませんか? char buf[] = "明日\t名詞,アシタ\nは\t助詞,ワ\n晴れ\t名詞,ハレ\n"; なのですから。 もう一度うかがいます。 なぜ、while の中の最初の strtok の区切り文字を "\t" にしたいのですか?
KZK13

2020/08/21 05:06

>>なぜ、while の中の最初の strtok の区切り文字を "\t" にしたいのですか? 好奇心ゆえに一部分だけ"\t"にしたらどうなるのか試したいと思ったためです。
DreamTheater

2020/08/21 05:28

> 好奇心ゆえに一部分だけ"\t"にしたらどうなるのか試したいと思ったためです。 質問文から削除するか、注釈を入れるのが最低限のマナーではないですか?非常識です! あなたは興味本位でも我々回答者には大変な迷惑行為です。
fana

2020/08/21 06:16

●strtokを使うためにやるべきこと: strtokの仕様を調べる→仕様を理解する→理解した通りに使う ●上記を行った結果,不明点が生じて他人に訊く場合に示すべき情報: ・自分の理解ではstrtokとはこれこれこういう仕様である ・だから,このデータに対する想定される結果というのはこうであるべきと考える ・しかし実際の結果はこうであった
guest

0

maisumakunさんも仰ってますが、このstrtokの実装では複数トークンに分割されません。

strtok実行後『は』『晴れ』がNULLになる変数を、要所要所でPrintf等でスナップショットを出力してもう少し問題部分を絞り込んでから投稿してください。

これではデバッグしろと言ってるのと同じです。

投稿2020/08/05 23:25

DreamTheater

総合スコア1095

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

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

0

「明日は晴れ」と入力した際に

この入力と区切り文字だと、そもそもトークンに分かれないかと思います。

「『は』『晴れ』がNULLになる」とはどういう意味でしょうか?変数名などでもう少し具体的に説明していただければと思います。

投稿2020/08/05 23:03

maisumakun

総合スコア146018

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

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

Zuishin

2020/08/05 23:43

本人 MeCab と strtok の区別もついていないのでこれに答えるのは難しいと思います。 質問を翻訳すると、質問からリンクされたコードを調べて直してくれというデバッグ依頼になります。この内容は前の質問と同じですが、そこで相手にされなくなったので焼き直して新しく質問しています。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問