【问题标题】:How and why strtok remembers string?strtok 如何以及为什么记住字符串?
【发布时间】:2014-02-26 13:31:48
【问题描述】:

在下面的 C 程序中,strtok 用于分割字符串。程序给出了例外的输出,但我无法理解它是如何工作的。

首先,我们将string 传递给tokenizedelimiter。但是在以后的迭代中,我们只是传递了NULL如何为什么函数记住字符串?

如果我想同时对不同的字符串使用标记化怎么办?

#include "stdafx.h"
#include <cstdio>
#include <cstring>

int main(int argc, char* argv[])
{
    char arr[] = "This is string to split";

    char * subStr = new char[10];
    subStr = strtok(arr, " ");

    while (subStr)
    {
        printf("%s\n", subStr);
        subStr = strtok(NULL, " ");
    }

    return 0;
}

输出:

This
is
string
to
split

【问题讨论】:

  • 如果它不记得如何知道要返回哪个令牌,第一个或第二个或最后一个。
  • @MadHatter,有办法,我们可以一次又一次地传递新字符串。问题是,目前的方法不能让它一次只对一个字符串起作用。
  • 对于“它的记忆方式”可能是static pointer。我不认为可以使用动态分配,因为它什么时候知道free内存。
  • 方法是……(不记得什么)

标签: c string split strtok libc


【解决方案1】:

strtok 函数有一个内部状态,它会记住它到达的最后一个位置。由于它通过用零替换标记来覆盖原始字符串,因此它只需要记住字符串中的下一个位置。如果您使用非空字符串参数调用strtok,则内部状态将重置为新字符串。因此,确实,您不能一次在多个字符串上使用它,只能一个接一个地使用它。 (某些平台提供可重入变体strtok_r,它允许您传递自己的状态变量。)

这是一个示例实现:

char * my_strtok(char * in, char delim)   // not quite the same signature
{
    _Thread_local static char * pos = NULL;

    if (in) { pos = in; }

    char * p = find_next_delimiter(pos, delim);    // NULL if not found
    if (p) { *p = '\0'; ++p; pos = p; }

    return p;
}

(真正的strtok 搜索给定列表的任何分隔符,并且还会跳过空字段。)这种可重入变体将用函数参数替换静态变量pos

【讨论】:

    【解决方案2】:

    strtok 函数使用静态变量来跟踪先前调用的状态。出于这个原因,它不是线程安全的(请查看strtok_r),您不应该使用它同时标记不同线程上的不同字符串。

    Here 是它可能实现的一种方式。

    【讨论】:

    • 注意:strtok_r 是非标准的,并非在每个平台上都可用。
    • 为什么它不是线程安全的?内部状态是线程本地的,这完全有可能,就像errno
    • @KerrekSB 好点。它可以。但它可以保证在某个地方如此吗? (我不知道——老实说。)如果不是,我想知道我正在使用的版本的细节。 (例如,我包含的那个链接让我相信应该小心......)在必须做出假设的情况下,我选择假设它不是而不是它。
    • @Turix:你说得对,C11 标准甚至明确表示不需要避免数据竞争。 (相比之下,errno 必须是线程本地的。)有一个可选的标准化 strtok_s 具有状态参数。
    【解决方案3】:

    “如何”= 使用静态变量。

    "Why" = for continue 需要在零之后的下一个,如果您再次传递原始字符串 - 您将需要再次跳过第一个令牌,这会占用 CPU 周期

    【讨论】:

      【解决方案4】:

      strtok 如何以及为什么记住字符串?

      strtok() 函数在解析时使用静态缓冲区。

      如果我想同时对不同的字符串使用标记化怎么办?

      您可以自己构建:

      #include <stdio.h>
      #include <string.h>
      
      char *scan(char **pp, char c)
      {
          char *s = *pp, *p;
      
          p = strchr(*pp, c);
          if (p) *p++ = '\0';
          *pp = p;
          return s;
      }
      
      int main(void)
      {
          char s[] = "This is string to split";
          char *p = s;
      
          while (p) {
              printf("%s\n", scan(&p, ' '));
          }
          return 0;
      }
      
      • 请注意,scan() 将原始字符串中所有出现的分隔符替换为 \0

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多