C中的strtok函数如何工作?

时间:2014-01-13 17:11:46

标签: 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, " ,.-");如何返回新令牌?我的意思是,我们使用strtok致电NULL。这对我来说没有多大意义。

4 个答案:

答案 0 :(得分:36)

有关strtok的两件事情。如前所述,它“保持内部状态”。此外,它弄乱你喂它的字符串。从本质上讲,它会在找到您提供的令牌时写入'\0',并返回指向字符串开头的指针。在内部,它维护最后一个令牌的位置;下次你打电话给它,就从那里开始。

重要的推论是,您无法在strtok类型的字符串上使用const char* "hello world";,因为修改const char*字符串的内容时会出现访问冲突。

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
                              ^

希望它有意义。

答案 1 :(得分:2)

strtok维持内部状态。当您使用非NULL调用它时,它会重新初始化自己以使用您提供的字符串。当你用NULL调用它时,它会使用该字符串,以及当前获得的任何其他状态返回下一个标记。

由于strtok的工作方式,如果您正在编写多线程应用程序,则需要确保链接到C运行时的多线程版本。这将确保每个线程获得strtok的内部状态。

答案 2 :(得分:2)

strtok()函数在调用之间存储数据。当您使用NULL指针调用它时,它会使用该数据。

来自http://www.cplusplus.com/reference/cstring/strtok/

  

找到最后一个标记的点由内部保留,用于下次调用时使用的函数(不需要特定的库实现来避免数据争用)。

答案 3 :(得分:0)

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 */
}