【问题标题】:strtok/strtok_r quit parsing in the middlestrtok/strtok_r 中途退出解析
【发布时间】:2015-09-04 09:27:16
【问题描述】:

strtok_r 在解析时将空字符放入输入字符串的不同位置。只有在strtok_r 返回NULL 后才会恢复原始字符串。

如果我需要在长字符串开头附近的某处提取标记怎么办?如果我离开循环,输入字符串仍然断开。我可以尝试手动恢复分隔符,但我不知道它是否是最后一个标记。 问题是 saveptr 值没有记录。

void extract_nth_token(char *res, size_t reslen, char *str, const char *delim, int n) {
  int i;
  char *token;
  char *save_ptr;

  token = strtok_r(str, delim, &save_ptr);
  for(i = 0; token != NULL; i++) {
    token = strtok_r(NULL, delim, &save_ptr);
    if (i == n) {
      snprintf(res, reslen, "%s", token);
      /* token[strlen(token)] = delim[0]; */
      /* break; */
    }
  }
}

【问题讨论】:

  • The problem is that saveptr value is not documented.你在找什么样的value
  • 复制字符串并使用它。
  • @SouravGhosh 告诉我它是否是最后一个令牌的值
  • @basin save_ptr 是一个不透明的对象,它用于保存令牌解析器的内部状态。它与判断它是否是最后一个令牌无关。
  • 这看起来像是一个 XY 问题。恕我直言,最好的方法是避免使用 strtok() 和 strtok_r(),并使用手工步进器解析字符串,可能使用 strstr()、strchr()、strspn() 和 strcspn()。

标签: c performance tokenize strtok


【解决方案1】:

strtok() 和 strtok_r() 是很糟糕的函数:

  • 他们修改输入字符串
  • 它们将连续分隔符视为一个分隔符,这可能是在跳过空格时使用的,但在解析 .CSV(或制表符分隔)输入时却不是。

最好完全避免使用strtok()和strtok_r(),使用strspn()和strcspn()。下面的函数就是这样做的。返回值和snprintf()类似:找到的token中的字符个数(不包括终止NUL字节)

  • 如果没有令牌#n: '\0' 写入缓冲区并返回 0
  • 如果缓冲区对于找到的令牌加上终止 NUL 字节来说太小,则将“\0”写入缓冲区并返回令牌长度
  • 是否缓冲区足够大,写入token + '\0',并返回strlen(token)。

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

size_t extract_nth_token_ohne_strtok_r(char *res, size_t maxlen, const char *str, const char *delim, int n)
{
size_t pos, len;
int itok;

for (itok=0,pos=0; str[pos]; ) {
        len = strcspn(str+pos, delim);
        if (itok++ == n) {
                if (len < maxlen) memcpy(res, str+pos, len), res[len] = 0;
                else res[0] = 0;
                return len;
                }
        pos += len;
        if (str[pos]) pos++;
        }
res[0] = 0;
return 0;
}

int main(void)
{
char * omg = "zero one\ttwo \tfour\nfive" ;
char token[80];
size_t toklen;
int ii;

printf("\n## With a large enough buffer:\n" );
for (ii=0; ii < 7; ii++) {
        toklen = extract_nth_token_ohne_strtok_r(token, sizeof token
                , omg, " \t\n", ii);
        printf("%d: res=%zu \"%s\"\n" , ii, toklen, token );
        }

printf("\n## With 4-character buffer:\n" );
for (ii=0; ii < 7; ii++) {
        toklen = extract_nth_token_ohne_strtok_r(token, 4
                , omg, " \t\n", ii);
        printf("%d: res=%zu \"%s\"\n" , ii, toklen, token );
        }

return 0;
}

注意:如果您确实想将连续的空白视为一个,您可以将if (str[pos]) pos++;替换为:

pos += strspn(str+pos, delim);

【讨论】:

    猜你喜欢
    • 2014-04-08
    • 1970-01-01
    • 2011-10-23
    • 1970-01-01
    • 1970-01-01
    • 2016-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多