web-dev-qa-db-ja.com

Cのstrtok関数はどのように機能しますか?

strtok関数を説明する次のサンプルプログラムを見つけました。

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

int main ()
{
    char str[] ="- This, a sample string.";
    char * pch;
    printf ("Splitting string \"%s\" into tokens:\n",str);
    pch = strtok (str," ,.-");
    while (pch != NULL)
    {
        printf ("%s\n",pch);
        pch = strtok (NULL, " ,.-");
    }
    return 0;
}
_

ただし、これがどのように機能するかはわかりません。

pch = strtok (NULL, " ,.-");が新しいトークンを返す可能性はありますか。つまり、strtokwith NULLを呼び出しています。これは私にはあまり意味がありません。

20
user2426316

strtokについて知っておくべき2つのこと。述べたように、それは「内部状態を維持する」。また、それはフィードする文字列を台無しにするです。基本的に、それは'\0'を書き込み、指定したトークンを見つけて、文字列の先頭へのポインターを返します。内部的には、最後のトークンの場所を維持します。そして次にあなたがそれを呼ぶとき、それはそこから始まります。

重要な結果として、const char* "hello world";文字列の内容を変更するとアクセス違反が発生するため、const char*タイプの文字列ではstrtokを使用できません。

strtokの「良い」点は、実際には文字列をコピーしないことです。そのため、追加のメモリ割り当てなどを管理する必要はありません。ただし、上記を理解していないと、正しく使用できません。

例-「this、is、a、string」がある場合、strtokを連続して呼び出すと、次のようにポインタが生成されます(^は返される値です)。トークンが見つかった場所に'\0'が追加されることに注意してください。つまり、ソース文字列が変更されます。

t  h  i  s  ,  i  s  ,  a  ,  s  t  r  i  n  g \0         this,is,a,string

t  h  i  s  \0 i  s  ,  a  ,  s  t  r  i  n  g \0         this
^
t  h  i  s  \0 i  s  \0 a  ,  s  t  r  i  n  g \0         is
               ^
t  h  i  s  \0 i  s  \0 a  \0 s  t  r  i  n  g \0         a
                        ^
t  h  i  s  \0 i  s  \0 a  \0 s  t  r  i  n  g \0         string
                              ^

それが理にかなっていると思います。

37
Floris

strtokは内部状態を維持します。 NULL以外で呼び出すと、指定した文字列を使用するように再初期化されます。 NULLで呼び出すと、その文字列と、現在次のトークンを返すために取得した他の状態が使用されます。

strtokの動作方法により、マルチスレッドアプリケーションを作成している場合は、Cランタイムのマルチスレッドバージョンとリンクする必要があります。これにより、各スレッドがstrtokの独自の内部状態を取得できるようになります。

2
Sean

strtok()関数は、呼び出し間のデータを格納します。 NULLポインターで呼び出すときにそのデータを使用します。

From http://www.cplusplus.com/reference/cstring/strtok/ から:

最後のトークンが見つかったポイントは、次の呼び出しで使用される関数によって内部的に保持されます(特定のライブラリ実装は、データ競合を回避するために必要ありません)。

2
Andy Thomas

strtok関数は、すべてのスレッド間で共有される内部静的変数にデータを格納します。

スレッドの安全性のために strtok_r を使用する必要があります

から http://www.opensource.Apple.com/source/Libc/Libc-167/string.subproj/strtok.c

static char *last;をご覧ください

char *
strtok(s, delim)
    register char *s;
    register const char *delim;
{
    register char *spanp;
    register int c, sc;
    char *tok;
    static char *last;


    if (s == NULL && (s = last) == NULL)
        return (NULL);

    /*
     * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
     */
cont:
    c = *s++;
    for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
        if (c == sc)
            goto cont;
    }

    if (c == 0) {       /* no non-delimiter characters */
        last = NULL;
        return (NULL);
    }
    tok = s - 1;

    /*
     * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
     * Note that delim must have one NUL; we stop if we see that, too.
     */
    for (;;) {
        c = *s++;
        spanp = (char *)delim;
        do {
            if ((sc = *spanp++) == c) {
                if (c == 0)
                    s = NULL;
                else
                    s[-1] = 0;
                last = s;
                return (tok);
            }
        } while (sc != 0);
    }
    /* NOTREACHED */
}
0
Juan Ramirez