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只能分开令牌,而不是必须在两侧终止它们。