7

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

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

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

正如我们所知,在每个标记的末尾strtok()放置一个。\0但是在下面的程序中,最后一个分隔符是一个点 ( .),在该点和引号 ( ) 之间是Toad"。现在点是我程序中的分隔符,但Toad之后没有分隔符,甚至没有空格(这是我程序中的分隔符)。请清除由该前提引起的以下混淆:

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

一旦在对 strtok 的调用中找到了 str 的终止空字符,所有随后以空指针作为第一个参数的对该函数的调用都将返回空指针。

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

它没有说一旦遇到空字符,就会返回一个指向标记开头的指针(我们甚至没有标记,因为我们没有找到标记的结尾,因为没有找到分隔符从令牌的开头开始扫描后(即从 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;
}

输出:

猎鹰


秃鹰

鸽子
麻雀
母鸡
猫头鹰
蟾蜍

4

5 回答 5

9

(7.24.5.8)的标准规范strtok非常明确。如果我理解正确,特别是第 4 段(我添加的重点)与问题直接相关:

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

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

在通话中

char *where = strtok(string_or_NULL, delimiters);

返回的标记(指向它的指针) - 如果有的话 - 从从起始位置(包括)找到的第一个非分隔符字符延伸到下一个分隔符字符(不包括),如果存在,或者字符串的结尾,如果不存在后面的分隔符。

与标准相反,链接描述没有明确提到令牌延伸到字符串末尾的情况,因此在这方面它是不完整的。

于 2013-05-15T19:50:59.880 回答
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确实是一个令牌。一种思考方式是分隔符列表始终'\0'在分隔符字符串的末尾包含 NUL。


诊断出这一点后,请注意这strtok()不是一个好用的函数——它不是线程安全的或可重入的。在 Windows 上,您可以strtok_s()改用;在 Unix 上,您通常可以使用strtok_r(). 这些是更好的函数,因为它们不在内部存储要恢复搜索的指针。

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

strtok()函数族(以及相关的 )的另一个问题strsep()是它们覆盖了分隔符;在标记器对字符串进行标记后,您无法找出分隔符是什么。这在某些应用程序中可能很重要(例如解析 shell 命令行;分隔符是管道、分号还是 & 符号(或...)很重要。所以 shell 解析器通常不使用strtok(),尽管关于所以关于解析器使用的shell strtok()

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

于 2013-05-15T19:56:27.073 回答
2

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

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

于 2013-05-15T17:55:14.397 回答
0

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

于 2013-05-15T17:11:01.320 回答
0

您可能只是误读了描述吗?

一旦在对 strtok 的调用中找到了 str 的终止空字符,所有随后以空指针作为第一个参数的对该函数的调用都将返回空指针。

鉴于“后续”,我将其视为在发现strtok 之后\0的每次调用,不一定是当前调用本身。因此,该定义与行为一致(以及您对 的期望strtok)。

于 2013-05-15T17:14:12.360 回答