【问题标题】:Trying to understand strtok试图理解 strtok
【发布时间】:2011-01-14 02:26:18
【问题描述】:

考虑以下使用 strtok 拆分字符串 madddy 的 sn-p。

char* str = (char*) malloc(sizeof("Madddy"));
strcpy(str,"Madddy");

char* tmp = strtok(str,"d");
std::cout<<tmp;

do
{
    std::cout<<tmp;
    tmp=strtok(NULL, "dddy");
}while(tmp!=NULL);

它工作正常,输出是 Ma。但是通过将 strtok 修改为以下,

tmp=strtok(NULL, "ay");

输出变成了 Madd。那么 strtok 究竟是如何工作的呢?我有这个问题是因为我希望 strtok 将分隔符字符串中的每个字符都作为分隔符。但在某些情况下,它会这样做,但在少数情况下,它会产生意想不到的结果。谁能帮我理解这个?

【问题讨论】:

  • 老实说,我认为正确的做法是完全停止使用strtok。这是一个难以使用、难以调试的函数,根本没有线程安全保证。您可能最好使用string::findstring::substr 的某种组合来进行解析。
  • 出于重要性和重点,我愿意重复这一点,尤其是因为您使用的是 C++ 而不是 C。此外,您可能需要查看 boost::tokenize。
  • 您没有打印换行符或其他符号来分隔匹配的标记。 Madddy,带有分隔符 d 和 y(无需指定 d 三次),仅包含“Ma”标记和尾随分隔符。带有分隔符 a 和 y 的 Madddy 由标记“M”和“ddd”组成 - 不带空格打印它们,您会看到“Mddd”。你说你看过《疯狂》?我认为这是一个错字...?
  • @Tony:我认为 Karthick 是对的。这里的问题是 Karthick 没有在他的令牌之间使用分隔符,因此很难给出准确的答案。
  • 已将标签更改为 C。与通常更喜欢其他标记化形式的纯 C++ 开发人员相比,查看 C 标签的人可能更能提供帮助。

标签: c++ strtok


【解决方案1】:

“试图理解 strtok”祝你好运!

无论如何,我们都在 2011 年。正确地标记:

std::string str("abc:def");
char split_char = ':';
std::istringstream split(str);
std::vector<std::string> token;

for (std::string each; std::getline(split, each, split_char); token.push_back(each));

:D

【讨论】:

  • 是的,我意识到这并没有严格回答这个问题。但 IMO 它是一个有价值和优越的选择,所以我认为它仍然值得回答。
  • 我完全意识到这个问题.. 但是我只是对标准规范感到好奇!
【解决方案2】:

Fred Flintstone 可能使用过strtok()。它早于多线程环境并击败(修改)源字符串。

当第一个参数使用NULL 调用时,它会继续解析最后一个字符串。这个功能很方便,但即使在当时也有点不寻常。

【讨论】:

    【解决方案3】:

    其实你的代码是错误的,难怪你会得到意想不到的结果:

    char* str = (char*) malloc(sizeof("Madddy"));
    

    应该是

    char* str = (char*) malloc(strlen("Madddy") + 1);
    

    【讨论】:

    • 是的,第一个示例可能分配了 4 个字节(在 32 位环境中),这是一个指针的大小。像"abcdefghijkm" 这样的字符串常量的类型是指针(具体是char *const char *,具体取决于编译器)。
    • @wallyk:实际上,it doesn't。字符串文字的类型是 char 数组。 @Anders:虽然代码很奇怪,但你的代码完全相同。
    • @Fred Nurk:哦,是的。这似乎打破了模式,可能更有用。长期以来,我一直避免使用它。 (对于上面两个cmets的错误信息,我们深表歉意。)
    • 最好避免使用sizeof,在这种情况下,您很容易对自己实际在做什么感到困惑。比较喜欢写一个数组长度的模板函数,那你肯定不会出错:template &lt;typename T, size_t N&gt; size_t array_size(const (T&amp;)[N]) { return N; }.
    【解决方案4】:

    您似乎忘记了您第一次(在循环外)通过分隔符“d”调用了 strtok。

    strtok 工作正常。你应该有一个参考here

    对于第二个例子(strtok("ay")):

    首先,调用 strtok(str, "d")。它将查找第一个“d”,并分隔您的字符串。具体来说,它设置 tmp = "Ma" 和 str = "ddy"(删除第一个 "d")。

    然后,调用 strtok(str, "ay")。它将在 str 中查找“a”,但由于您的字符串现在只是“ddy”,因此不会发生匹配。然后它会寻找一个“y”。所以 str = "dd" 和 tmp = ""。

    如您所见,它会打印“Madd”。

    【讨论】:

    • @Karthick:第一个例子有效,但它可能不像你想象的那样有效。我建议不要使用 cout
    • 请告诉我,同一段代码被另一个循环包围怎么办,有没有办法重新初始化 strtok ?
    【解决方案5】:

    我问了一个问题,灵感来自另一个关于 functions causing security problems/bad practise functions and the c standard library 的问题。

    引用那里给我的答案:

    strtok() 的常见陷阱 函数是假设解析的 字符串保持不变,而它 实际上替换了分隔符 带有'\0' 的字符。

    另外,strtok() 用于制作 随后调用它,直到 整个字符串被标记化。一些 库实现存储 strtok()的内部状态 全局变量,这可能会导致一些 令人讨厌的惊喜,如果 strtok() 是 从多个线程调用 同一时间。

    既然您已经标记了您的问题 C++,请使用其他内容!如果您想使用 C,我建议您实现自己的以安全方式工作的分词器。

    【讨论】:

      【解决方案6】:

      由于您将标记更改为 C 而不是 C++,因此我重写了您的函数以使用 printf,以便您可以看到发生了什么。黄是对的。您看到正确的输出,但我认为您在同一行打印所有内容,因此您对输出感到困惑。看看 Hoang 的回答,他正确地解释了正在发生的事情。此外,正如其他人所指出的那样, strtok 会破坏输入字符串,因此您必须小心这一点 - 而且它不是线程安全的。但是,如果您需要一个快速的脏标记器,它可以工作。此外,我更改了代码以正确使用 strlen,而不是 Anders 正确指出的 sizeof。

      这是您的代码修改为更像 C:

      char* str = (char*) malloc(strlen("Madddy") + 1);
      strcpy(str,"Madddy");
      
      char* tmp = strtok(str,"d");
      printf ("first token: %s\n", tmp);
      
      do
      {
          tmp=strtok(NULL, "ay");
          if (tmp != NULL ) {
             printf ("next token: %s\n", tmp);
          }
      } while(tmp != NULL);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-04-06
        • 2023-03-07
        • 2011-03-28
        • 2011-11-27
        • 2017-08-11
        • 1970-01-01
        • 2013-08-18
        相关资源
        最近更新 更多