Hatena::ブログ(Diary)

ICPC突破専用ザク このページをアンテナに追加

2010.March.02

strtok(区切り文字による文字列の切り分け)

STLのstringで,str="100 200 400"のような文字をスペースで切り分けて,a=100,b=200,c=400...のようにしたいことがある.

この場合,str.split(" "); のようにすれば適当に切り分けてvector<string>にでも出力してくれるんじゃないかと思っていたのだけど,調べたところsplitのようなものはどうも無いっぽく,代わりにCでstrtokという関数があるそうなのでこれを利用する.

(stringから使う場合は(char*)str.c_str();する)


strtokはstring.hの関数で,以下のような挙動をする.

http://www9.plala.or.jp/sgwr-t/lib/strtok.html

【書式】

#include <string.h>

char *strtok(char *s1, const char *s2);

【説明】

分解対象文字列 s1 を、文字群 s2 中の文字を区切りに字句(トークン)に分解します。

次のような手順で strtok() を用いて文字列をトークンに分解することが出来ます。

1. 最初の呼び出しでは s1 には分解対象の文字列を指定します。トークンがあれば、strtok() はトークンへのポインタを返却します。

2. 2回目以降の呼び出しでは s1 に NULL を指定します。分解できるトークンがあるうちは、strtok() はトークンへのポインタを返却します。

3. トークンがなくなると strtok() は NULL を返却します。

strtok() はトークンへのポインタを返却しながら、分解対象文字列 s1 中の区切り文字に空文字('\0')を埋めていきます。ですから、s1 に文字列リテラルや変更されては困る文字列を指定してはいけません。


つまり,1回目の切り分けでは strtok(str, " "); のようにstrを指定して,2回目以降では strtok(NULL, " "); のようにNULLを指定する.

strtokによる戻り値はトークンへのポインタ(トークンが無くなるとNULL)が返る.

なので,たとえばstrを" "切り分けた結果を出力するコードは以下のようになる.


#include <stdio.h>
#include <string.h>

int main()
{
    char str[] = "100 200 400";
    char *tp;

    tp = strtok(str," ");
    puts(tp);
    while (tp!=NULL) {
        tp = strtok(NULL," ");
        if (tp!=NULL) puts(tp);
    }
}

ただ,これだと1回目と2回目以降で処理の記述が重複してあまり美しくないので,次のように書くといいんじゃないかと思った.

#include <cstdio>
#include <cstring>


int main() {
    char str[]="100 200 400";

    char *tp;
    for (int tc=0;;) {
        tp=strtok((tc++==0)?str:NULL, " ");
        if (tp!=NULL) puts(tp);
        else break;
    }
}

要するにループが初回なら引数をstrに,2回目以降ならNULLを引数に指定しているというだけ.

ネット上のサンプルを見てると初回と2回目以降で2度strtokを書いているものが多くて少し気になったのでメモ.

se-kichise-kichi 2010/03/02 18:18 boostにsplitがあった気が。(TopCoderとかそっち系で使えんのかは知らないけど…)
http://www.kmonos.net/alang/boost/classes/string_algo.html

ir5ir5 2010/03/02 19:56 boostはなんか魔界っぽくて怖いので忌避してましたわ…
boost使える環境ならそっちの方がいいですね.

mattnmattn 2010/03/04 10:07 こんな感じのでいいんじゃないでしょうか。

http://gist.github.com/321280

ir5ir5 2010/03/04 13:46 > id:mattn
stringのfindとsubstrを組み合わせてもできますが,標準関数に既にあるならそっちを使ったほうが良いのではないかな,と.
その方が実装も楽でバグも出にくいでしょうし.

mattnmattn 2010/03/04 23:19 strtokは内部でトークン全体を保持するのでマルチスレッドで使えない(ランタイムによっては対応あり)ので、OSSなんかを見る限りでは使われない事の方が多い気がしますね。:-)

codercoder 2010/03/05 14:23 スペースで区切るだけだったらstringstreamが一番手っ取り早いかもしれませんね
http://taichino.com/programming/1238

ir5ir5 2010/03/06 03:06 > id:mattn
あー,実装で静的な領域を使っている分ある程度制約があるんですね.
その場合はstrtok_rを使うことになるらしいですが.

> coder
おお,これは便利そう.
これならc_str()してstrtokしてatoiして… とかするよりもstringstreamを使う方が楽そうですね.

トラックバック - http://d.hatena.ne.jp/ir5/20100302/1267520515