strtok()问题:如果标记由分隔符分隔,为什么分隔符和空’\ 0’之间的最后一个标记?

在下面的程序中, strtok()在主要部分按预期工作,但我无法理解一个发现背后的原因。 我读过关于strtok()

为了确定令牌的开始和结束,该函数首先从起始位置扫描未包含在分隔符中的第一个字符(它成为令牌的开头)。 然后从令牌的这个开头开始扫描包含在分隔符中的第一个字符,这将成为令牌的结尾。

资料来源: http : //www.cplusplus.com/reference/cstring/strtok/

而且我们知道, strtok()在每个令牌的末尾放置一个\0 。 但是在下面的程序中,最后一个分隔符是一个点( . ),之后在该点和引号( " )之间有Toad 。现在,dot是我程序中的分隔符,但在Toad之后没有分隔符,甚至不是白色空间(这是我程序中的分隔符)。请清除以下由此前提产生的混乱:

为什么strtok()Toad视为令牌,即使它不在2个分隔符之间? 这是我在遇到NULL字符( \0 )时读到的关于strtok() )的内容:

一旦在strtok的调用中找到了str的终止空字符,所有后续调用此函数并使用空指针作为第一个参数返回空指针。

资料来源: http : //www.cplusplus.com/reference/cstring/strtok/

没有任何地方说过一旦遇到一个空字符,就会返回一个指向令牌开头的指针(我们甚至没有令牌,因为我们没有得到令牌的结尾,因为没有找到分隔符字符在从令牌的开头开始扫描之后(即从Toad的’T’开始),我们只发现了一个空字符, 而不是一个分隔符 。 那么为什么参数字符串的最后一个分隔符和引号之间部分strtok()视为一个标记? 请解释一下。

码:

 #include  #include  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; } 

输出:






鸽子
麻雀
母鸡
猫头鹰
蟾蜍

标准的strtok规范(7.24.5.8)非常清楚。 特别是第4段(我强调)与问题直接相关,如果我理解正确的话:

3序列中的第一个调用将搜索s1指向的字符串,查找s2指向的当前分隔符字符串中未包含的第一个字符。 如果没有找到这样的字符,则s1指向的字符串中没有标记, strtok函数返回空指针。 如果找到这样的字符,则它是第一个标记的开头。

4然后, strtok函数从那里搜索当前分隔符字符串中包含的字符。 如果未找到此类字符,则当前标记将扩展到s1指向的字符串的末尾,随后对标记的搜索将返回空指针 。 如果找到这样的字符,它将被空字符覆盖,该字符终止当前令牌。 strtok函数保存指向以下字符的指针,从该字符开始下一次搜索令牌。

在通话中

 char *where = strtok(string_or_NULL, delimiters); 

返回的令牌(指向的指针) – 如果有的话 – 从起始位置(包括)找到的第一个非定界符延伸到下一个定界符(不包括),如果存在,或者字符串的结尾,如果没有以后的分隔符字符。

链接描述没有明确提到延伸到字符串结尾的标记的情况,而不是标准,因此在这方面它是不完整的。

转到POSIX for 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() 。 这些是更好的function,因为它们不会在内部存储搜索要恢复的指针。

因为strtok()不是可重入的,所以在使用strtok() ,你不能从一个本身使用strtok()的函数内调用一个使用strtok()的函数。 此外,任何使用strtok()库函数都必须清楚地标识为这样做,因为无法从使用strtok()的函数调用它。 所以,使用strtok()会让生活变得艰难。

strtok()函数系列的另一个问题(与strsep()相关)是它们覆盖分隔符; 在令牌化器对字符串进行标记后,您无法找到分隔符的内容。 这在某些应用程序中很重要(例如解析shell命令行;分隔符是管道还是分号或符号(或……)都很重要。所以shell解析器通常不使用strtok() ,尽管数量很多关于解析器使用strtok() shell的问题。

通常,您应该避开普通的strtok() ,由您决定strtok_r()strtok_s()是否适合您的目的。

因为cplusplus.com并没有告诉你整个故事。 Cppreference.com有更好的描述。

Cplusplus.com也没有提到strtok不是线程安全的,只记录了C ++编程语言的strtok函数,而cppreference.com确实提到了线程安全问题并记录了C和C ++编程的strtok函数语言。

您是否只是误读了描述?

一旦在strtok的调用中找到了str的终止空字符,所有后续调用此函数并使用空指针作为第一个参数返回空指针。

鉴于’后续’,我正在阅读这个,因为每次调用strtok 之后都会发现\0 ,不一定是当前的一个。 因此,定义与行为(以及您对strtok期望)一致。

strtok将字符串分解为一系列标记,由给定的分隔符分隔。 Delimeters只能分开令牌,而不是必须在两侧终止它们。