【问题标题】:Nested strtok function problem in C [duplicate]C中的嵌套strtok函数问题[重复]
【发布时间】:2011-06-09 07:06:56
【问题描述】:

我有一个这样的字符串:

a;b;c;d;e
f;g;h;i;j
1;2;3;4;5

我想逐个元素地解析它。我使用了嵌套的 strtok 函数,但它只是拆分第一行并使 null 成为令牌指针。我该如何克服呢?代码如下:

token = strtok(str, "\n");

while(token != NULL && *token != EOF)
{
    char a[128], b[128];
    strcpy(a,token);
    strcpy(b,a);
    printf("a:%s\n",a);
    char *token2 = strtok(a,";");
    while(token2 != NULL)
    {
        printf("token2 %s\n",token2);
        token2 = strtok(NULL,";");
    }
    strcpy(token,b);
    token = strtok(NULL, "\n");
    if(token == NULL)
    {
        printf("its null");
    }
}

输出:

token 2 a
token 2 b
token 2 c
token 2 d
token 2 e

【问题讨论】:

  • 你确定你已经阅读了所有的行吗?
  • 是的,我确定我在 str 中阅读过它们。

标签: c nested token tokenize strtok


【解决方案1】:

strtok() 无法做到这一点;如果可用,请使用 POSIX 的 strtok_r() 或 Microsoft 的 strtok_s(),或者重新考虑您的设计。

char *strtok_r(char *restrict s, const char *restrict sep,
               char **restrict lasts);
char *strtok_s(char *strToken, const char *strDelimit, char **context); 

这两个功能可以互换。

请注意,变体 strtok_s() 在 C11 的可选部分中指定(ISO/IEC 9899:2011 中的附件 K)。但是,除了 Microsoft 之外,很少有供应商在该标准的该部分实施了接口。附件 K 中指定的 strtok_s() 版本与 Microsoft 的 strtok_s() 具有不同的界面——类似的问题困扰着附件 K 中指定的许多其他功能。

使用 strtok_r()

#include <string.h>
#include <stdio.h>

int main(void)
{
    char str[] = "a;b;c;d;e\nf;g;h;i;j\n1;2;3;4;5\n";
    char *end_str;
    char *token = strtok_r(str, "\n", &end_str);

    while (token != NULL)
    {
        char *end_token;
        printf("a = %s\n", token);
        char *token2 = strtok_r(token, ";", &end_token);
        while (token2 != NULL)
        {
            printf("b = %s\n", token2);
            token2 = strtok_r(NULL, ";", &end_token);
        }
        token = strtok_r(NULL, "\n", &end_str);
    }

    return 0;
}

结果

a = a;b;c;d;e
b = a
b = b
b = c
b = d
b = e
a = f;g;h;i;j
b = f
b = g
b = h
b = i
b = j
a = 1;2;3;4;5
b = 1
b = 2
b = 3
b = 4
b = 5

没有 strtok_r()

这适用于上下文 - 前提是数据以换行符结尾。

#include <string.h>
#include <stdio.h>

int main(void)
{
    char data[] = "a;b;c;d;e\nf;g;h;i;j\n1;2;3;4;5\n";
    char *string = data;
    char *token  = strchr(string, '\n');

    while (token != NULL)
    {
        /* String to scan is in string..token */
        *token++ = '\0';
        printf("a = %s\n", string);
        char *token2 = strtok(string, ";");
        while (token2 != NULL)
        {
            printf("b = %s\n", token2);
            token2 = strtok(NULL, ";");
        }
        string = token;
        token = strchr(string, '\n');
    }

    return 0;
}

输出

a = a;b;c;d;e
b = a
b = b
b = c
b = d
b = e
a = f;g;h;i;j
b = f
b = g
b = h
b = i
b = j
a = 1;2;3;4;5
b = 1
b = 2
b = 3
b = 4
b = 5

【讨论】:

  • 我无法使用它。还有其他解决方案吗?
  • @mausmust:那你就倒霉了——你必须编写自己的解决方案来标记你的字符串,因为strtok一次只能处理一个字符串。
【解决方案2】:

strtok_r 是最好和最安全的解决方案,但也有一种方法可以使用strtok

#include <string.h>
#include <stdio.h>

int main ()
{
  char str[] = "a;b;c;d;e\nf;g;h;i;j\n1;2;3;4;5\n";
  char *line;
  char *token;
  char buf[256];

  for (line = strtok (str, "\n"); line != NULL;
       line = strtok (line + strlen (line) + 1, "\n"))
    {
      strncpy (buf, line, sizeof (buf));
      printf ("Line: %s\n", buf);
      for (token = strtok (buf, ";"); token != NULL;
       token = strtok (token + strlen (token) + 1, ";"))
    {
      printf ("\tToken: %s\n", token);
    }
    }

  return 0;
}

输出:

Line: a;b;c;d;e
    Token: a
    Token: b
    Token: c
    Token: d
    Token: e
Line: f;g;h;i;j
    Token: f
    Token: g
    Token: h
    Token: i
    Token: j
Line: 1;2;3;4;5
    Token: 1
    Token: 2
    Token: 3
    Token: 4
    Token: 5

【讨论】:

  • +1: Devious...for 循环本质上是使用strtok() 作为strchr() 的组合,并在找到它的位置写入 NUL '\0'。它之所以有效,是因为您制作并剖析了该行的副本;否则,您必须在循环顶部调用 strlen(line) 并保存该长度以将其添加到 for 循环控件第三部分的行中。
  • strtok_r 不是最好的,因为它不是 C 标准(不是 C89 也不是 C99)
  • @user411313: 如果有的话最好:-)
猜你喜欢
  • 2021-11-29
  • 2021-12-30
  • 2021-04-03
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-12
  • 2015-12-29
相关资源
最近更新 更多