C语言中的strtok函数如何工作?

28

我找到了一个样例程序,它解释了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, " ,.-"); 会返回一个新的标记。 我的意思是,我们正在使用 NULL 调用 strtok。 对我来说这没有太多意义。


4
http://en.cppreference.com/w/c/string/byte/strtok - Sander De Dycker
我找到了一个样例程序,它解释了strtok函数。但是实际上并不是这个示例程序在解释,而是文档,你可能想要在这里阅读:http://man7.org/linux/man-pages/man3/strtok.3.html。 - alk
2
而且这对任何人来说都没有意义...因此创建了strtok_r()函数... - vrdhn
顺便说一下,这是“int main(void)”函数。 - alk
4个回答

58
关于 strtok,有两件事情需要知道。如前所述,它“维护内部状态”。此外,它会“破坏”您提供给它的字符串。实际上,它会在找到您提供的标记时写入一个 '\0',并返回指向字符串开头的指针。它在内部维护上一个标记的位置; 下次调用时,从那里开始。
重要的推论是,您不能在 const char* "hello world"; 类型的字符串上使用 strtok,因为修改 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
非常具有说明性,谢谢。这正是我正在努力解决的问题。 - Michael Iyke

4

strtok 维护内部状态。当您使用非NULL参数调用时,它会重新初始化自己以使用您提供的字符串。 当您使用NULL参数调用时,它将使用该字符串和任何其他状态来返回下一个 token。

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


2
strtok() 函数会在调用之间存储数据,并在使用 NULL 指针调用它时使用该数据。
来自 http://www.cplusplus.com/reference/cstring/strtok/
“上次找到标记的位置由函数内部保留,以便在下一次调用时使用(特定库实现不需要避免数据竞争)。”

1
大多数现代运行时将状态存储在线程本地存储中。这意味着它是线程安全的,但在重新进入时使用时不安全。 - David Heffernan
感谢您的纠正。 - Andy Thomas

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

Windows 上的 strtok_s - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接