strtok()の動作

strtok()は、標準ライブラリ関数の中でもかなり癖のある関数です。細かくstrtok()関数の動作を見てみましょう。

strtok()関数は次のような仕様になっています。

strtok()
char* strtok(char* str,const char* delim)
標準ライブラリ関数/string.h
引数
  1. 分割対象文字列str(2度目以降はNULLを指定)
  2. 区切り文字セットdelim
機能文字列strを、文字列delimに含まれるいずれかの文字のうち、最初に出てきたものの部分で分割する。
返却値文字列strの文字列delimに含まれない最初の文字へのポインタを返す。文字列delimに含まれる文字が見つからない場合には、NULLを返す。
注意文字列strはdelimに含まれる文字の部分に\0が埋め込まれるため、書き換えられる。2度目以降の呼び出しでは、1番目の引数をNULLとして呼び出すことで、前回埋め込まれた\0に続く部分からの文字列を指定することができる。
1053253151<>もけっけ<>blue<><>てすと<>127.0.0.1<><>

上のような文字列を<で区切ってポインタ配列に分けていくことを考えます。この文字列がstrtok()関数によってどう処理されるのかを細かく見ていきましょう。

char str[] = "1053253151<>もけっけ<>blue<><>てすと<>127.0.0.1<><>";

というように、strという名のchar配列にログの文字列を代入するとします。この文字列をstrtok()関数で分割するとどうなるでしょうか。区切り文字は'<'とします。また、stotok()関数が返すポインタをpとします。

char* p;
p = strtok(str,"<");

これが一度目の分割です。こうすると文字列strはどうなるでしょうか。

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

int main(void)
{
    char str[] = "1053253151<>もけっけ<>blue<><>てすと<>127.0.0.1<><>";
    char* p;
    size_t i,len = strlen(str);

    p = strtok(str,"<"); /* 分割 */
    /* 一文字ずつ表示 */
    for(i=0;i<len;i++){
        putchar(*p);
        p++;
    }

    return 0;
}

このプログラムの実行結果は、画面に表示させると処理系によって違ったものになるので、ファイルに出力したものをバイナリエディタを使って見てみましょう。画面はフリーのバイナリエディタBZのものです。

strtok()を一度実行後の文字列
1053253151<>もけっけ<>blue<><>てすと<>127.0.0.1<><>

処理する前のデータを画像のすぐ下に再び示したので、バイナリエディタ上の画面と比べてみてください。最初に現れた区切り文字'<'のところ(画像では00000A)の値が00になっていますね。

避けて通れない文字列の話で述べたように、この00というコードは文字列の終了を表します。strtok()関数が実行されると、2番目の引数で指定した区切り文字セットのいずれかの文字を見つけたとき、そこに文字列終了子(\0)を埋め込みます。もし文字列が区切り文字セットに含まれているいずれかの文字から始まっている場合は、それを省略して区切り文字セットにない最初の文字を文字列の先頭とします。

strtok()関数が返却する値は、文字列の先頭へのポインタです。とすると、ここで問題が生じないでしょうか。'\0'を埋め込んで文字列を区切り文字の位置で終わりとし、戻り値として文字列の先頭アドレスを返却します。では、strtok()を実行した後、区切り文字より後の文字列にアクセスするにはどうすれば良いのでしょうか。

strlen()関数で文字列の長さを調べてその分(+1回)ポインタを進めてみましょうか。それでもできないことはないのですが、strtok()関数はそういう使い方はしません。なぜなら、2回目の呼び出しからは1番目の引数にNULLを指定することで、前回に続く文字列が処理できるからです。これは、strtok()関数が関数の内部にstatic変数を持っていて、その変数に前回処理した文字列の、'\0'を埋め込んだ位置が保存されているからです。

ということで、strtok()関数の特徴と注意事項をまとめてみます。


Index Page