在以下程序中,strtok()
在主要部分中按预期工作,但我无法理解一个发现背后的原因。我已经阅读了strtok()
:
要确定令牌的开头和结尾,该函数首先从起始位置扫描未包含在分隔符中的第一个字符(它成为令牌的开头)。然后从令牌的开头开始扫描分隔符中包含的第一个字符,这将成为令牌的结尾。
正如我们所知,strtok()
在每个令牌的末尾放置了\0
。但是在下面的程序中,最后一个分隔符是一个点(.
),之后在该点和引号("
)之间有 Toad 。现在点是我程序中的分隔符,但是在 Toad 之后没有分隔符,甚至没有空格(在我的程序中是分隔符)。请清除以下因此前提出的混淆:
为什么strtok()
将 Toad 视为令牌,即使它不在2个分隔符之间?这是我在遇到NULL字符(strtok()
)时读到的\0
:
在strtok调用中找到str的终止空字符后,所有后续调用此函数并使用空指针作为第一个参数返回空指针。
没有任何地方说过一旦遇到空字符,就会返回一个指向令牌开头的指针(我们这里甚至没有令牌,因为我们没有得到令牌的结束,因为没有在从令牌开头开始扫描之后找到的分隔符(即来自Toad的'T'),我们只找到一个空字符,不是分隔符)。那么为什么参数字符串的最后一个分隔符和引号之间的部分被strtok()
视为一个标记?请解释一下。
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] =" Falcon,eagle-hawk..;buzzard,gull..pigeon sparrow,hen;owl.Toad";
char * pch=strtok(str," ;,.-");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ;,.-");
}
return 0;
}
猎鹰
鹰
鹰
秃鹰
鸥
鸽子
麻雀
母鸡
猫头鹰
蟾蜍
答案 0 :(得分:9)
标准的strtok
(7.24.5.8)规范非常清楚。特别是第4段(我强调)与问题直接相关,如果我理解正确的话:
3序列中的第一个调用搜索
s1
指向的字符串,查找s2
指向的当前分隔符字符串中未包含的第一个字符。如果找不到这样的字符,则s1
指向的字符串中没有标记,strtok
函数返回空指针。如果找到这样的字符,它就是第一个标记的开头。4
strtok
函数然后从那里搜索当前分隔符字符串中包含的字符。 如果未找到此类字符,则当前标记将扩展到s1
指向的字符串的末尾,随后对标记的搜索将返回空指针。如果找到这样的字符,它将被空字符覆盖,该字符终止当前令牌。strtok
函数保存指向以下字符的指针,从该字符开始下一次搜索令牌。
致电
char *where = strtok(string_or_NULL, delimiters);
返回的令牌(指向的指针) - 如果有的话 - 从起始位置(包括)找到的第一个非定界符扩展到下一个定界符(不包括),如果存在,或者结束如果没有以后的分隔符,则为字符串。
链接描述没有明确提到延伸到字符串结尾的标记的情况,而不是标准,因此在这方面它是不完整的。
答案 1 :(得分:4)
转到POSIX中strtok()
的说明,说明如下:
char *strtok(char *restrict s1, const char *restrict s2);
对
strtok()
的一系列调用将s1
指向的字符串分解为一系列标记,每个标记由s2
指向的字符串中的一个字节分隔。序列中的第一个调用将s1
作为其第一个参数,然后是使用空指针作为其第一个参数的调用。s2
指向的分隔符字符串可能与呼叫不同。序列中的第一个调用搜索
s1
指向的字符串,查找s2
指向的当前分隔符字符串中未包含的第一个字节。如果没有找到这样的字节,则s1
指向的字符串中没有标记,strtok()
将返回空指针。如果找到这样的字节,则它是第一个标记的开始。
strtok()
函数然后从那里搜索当前分隔符字符串中包含的字节。如果没有找到这样的字节,则当前标记扩展到s1
指向的字符串的末尾,随后对标记的搜索将返回空指针。如果找到这样的字节,它将被NUL字符覆盖,该字符终止当前令牌。strtok()
函数保存指向后续字节的指针,从该字节开始下一次搜索令牌。
注意第三段的第二句:
如果找不到这样的字节,则当前标记扩展到
s1
指向的字符串的末尾,随后对标记的搜索将返回空指针。
这清楚地表明,在问题的例子中,Toad
确实是一个标记。考虑它的一种方法是分隔符列表总是在分隔符字符串的末尾包含NUL '\0'
。
确认后,请注意strtok()
不是一个好用的函数 - 它不是线程安全的或可重入的。在Windows上,您可以使用strtok_s()
代替;在Unix上,您通常可以使用strtok_r()
。这些是更好的功能,因为它们不会在内部存储搜索将要恢复的指针。
由于strtok()
不可重入,因此您无法在使用strtok()
的情况下调用自身使用strtok()
的函数内使用strtok()
的函数。此外,任何使用strtok()
的库函数都必须清楚地标识为这样做,因为无法从使用strtok()
的函数调用它。因此,使用strtok()
会让生活变得艰难。
strtok()
函数族(与strsep()
相关的另一个问题)是它们覆盖了分隔符;在令牌化程序对字符串进行标记后,您无法找到分隔符的内容。这在某些应用程序中很重要(例如解析shell命令行;分隔符是管道还是分号或符号(或......)都很重要。所以shell解析器通常不使用strtok()
,尽管有关解析器使用strtok()
的shell的问题的数量。
一般情况下,您应该避开普通strtok()
,由您决定strtok_r()
或strtok_s()
是否适合您的目的。
答案 2 :(得分:2)
因为cplusplus.com并没有告诉你整个故事。 Cppreference.com有更好的描述。
Cplusplus.com也没有提到strtok
不是线程安全的,只记录了C ++编程语言的strtok
函数,而cppreference.com确实提到了线程安全问题和文档C和C++编程语言的strtok
函数。
答案 3 :(得分:0)
strtok将字符串分解为一系列标记,由给定的分隔符分隔。 分隔符只能分隔令牌,不一定要在两侧终止它们。
答案 4 :(得分:0)
您是否只是误读了描述?
在调用中找到str的终止空字符 strtok,所有后续使用空指针调用此函数 因为第一个参数返回一个空指针。
鉴于'后续',我在读取strtok
后发现\0
的{{1}} ,而不一定是当前的那个,我正在读这个。因此,定义与行为(以及您对strtok
的期望)一致。