在两个函数中使用strtok()是否安全?

时间:2016-03-17 07:27:06

标签: c

我使用strtok()将一行分解为单个单词并检查它们。如果找到那个东西,则调用另一个函数,该函数也必须使用strtok()

我知道strtok()不是可重入的。这是否意味着如果我在第二个函数中调用它,我在第一个函数中的字符串中的位置将会丢失?如果是这样,会在第一个函数中使用strtok()而在第二个函数中使用strtok_r()来解决问题吗?还有其他解决方案吗?

编辑: 谢谢。确实不可能在两个函数中使用strtok,但显然strtok_r不是标准的。重新设计它是......

4 个答案:

答案 0 :(得分:6)

由于strtok在内部使用全局变量来存储它在字符串中前进的程度,因此对strtok的混合调用将失败,就像您怀疑的那样。您的选择是:

  • 切换到strtok_r,它有一个类似的API,但不是标准的C(不过它在POSIX中);
  • 完全避免strtok支持其他一些不带隐藏全局状态的函数,例如strsep(也是非标准的);
  • 在调用另一个可以调用strtok的函数之前,请确保您的第一个函数完全耗尽strtok

总而言之,strtok是最好避免使用的功能。

答案 1 :(得分:1)

库函数strtok对当前解析位置使用内部静态:

  • 使用字符串调用时,会启动一个新的解析
  • 当使用NULL作为第一个参数调用时,它使用其内部状态。

如果您直接或间接从解析循环中调用strtok,内部状态将被更新,并且来自外部作用域的NULL调用将不会从先前状态继续,可能会调用未定义的行为

Posix函数strtok_r采用显式状态参数,因此可以在嵌套上下文中使用。如果您的系统上有此功能,请在您使用strtok的所有位置使用此功能。或者,您可以使用strchr()strcspn()

的其他方法

strtok_r在Posix中标准化。根据您的目标系统,它可能可用,也可能不可用。 MacOS和大多数Unix系统都符合Posix标准。 Windows可能以不同的名称使用它。如果它不可用,您可以在程序中重新定义它并有条件地编译它。

以下是您可以使用的简单实现:

char *strtok_r(char *s, const char *delim, char **context) {
    char *token = NULL;

    if (s == NULL)
        s = *context;

    /* skip initial delimiters */
    s += strspn(s, delim);
    if (*s != '\0') {
        /* we have a token */
        token = s;
        /* skip the token */
        s += strcspn(s, delim);
        if (*s != '\0') {
            /* cut the string to terminate the token */
            *s++ = '\0';
        }
    }
    *context = s;
    return token;
}

答案 2 :(得分:0)

Q1:

  

这是否意味着如果我在第二个函数中调用它,我在第一个函数中的字符串中的位置将会丢失?

A1:是的,确实如此。如果您这样做,那么将使用您提供的另一个字符串启动新的扫描序列,并且将丢失第一个字符串的后续调用的数据。

Q2:

  

如果是这样,会在第一个函数中使用strtok()而在第二个函数中使用strtok_r()解决问题吗?

A2:更好的方法是重做你的程序设计。

问题3:

  

还有其他解决方案吗?

A3:如果设计更改的成本太高,我建议保留第一个字符串的副本和包含最后找到的标记的指针。这可以允许您在完成第二个字符串后从最后一个位置继续(通过使用 strstr 获取启动指针)。

答案 3 :(得分:0)

http://en.cppreference.com/w/c/string/byte/strtok网站所示:

  

每次调用strtok都会修改一个静态变量:不是线程安全的。

所以是的,你不能同时从两个不同的函数调用这个函数(线程),也不能像下面那样调用它:

char input[] = "something that needs to be tokenize";
char *token = strtok(input, " ");
while(token) {
    puts(token);
    anotherfunction();
    token = strtok(NULL, " ");
}

void anotherfunction()
{
   char input[] = "another string needs to be tokenize";  
   char *tok = strtok(input, " ");
   while(tok) {
      puts(tok);
      tok = strtok(NULL, " ");
   }
}