我使用strtok()
将一行分解为单个单词并检查它们。如果找到那个东西,则调用另一个函数,该函数也必须使用strtok()
。
我知道strtok()
不是可重入的。这是否意味着如果我在第二个函数中调用它,我在第一个函数中的字符串中的位置将会丢失?如果是这样,会在第一个函数中使用strtok()
而在第二个函数中使用strtok_r()
来解决问题吗?还有其他解决方案吗?
编辑: 谢谢。确实不可能在两个函数中使用strtok,但显然strtok_r不是标准的。重新设计它是......
答案 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, " ");
}
}