带有无效定界符的字符串标记化

String tokenization with nullifying delimiters

strsep() 在其手册页中的描述指出,在到达第一个分隔符时,

This token is terminated by overwriting the delimiter with a null byte ('[=11=]'), and *stringp is updated to point past the token.

其中 *stringp 是输入字符串。

我想知道是否有任何函数可以简单地更新 *stringp 以指向令牌而不用 '[=11=]' 替换定界符。我有 3 个可能的定界符,我可以将其作为 " \t\n" 输入到 strsep(),所以像 strchr() 这样只会搜索单个定界符的东西将不起作用(至少效率不高) .由于我需要稍后打印出完整的字符串,否则我必须执行 memcpy() 以便稍后打印字符串。

(另外,谁能解释一下为什么这样实现..?)

下面是一个实现:

token = non_nulling_strsep(char** stringp, const char* delims);

strsep 非常相似,只是它将 *stringp 设置为实际终止标记的定界符,而不是后面的字符。与 strsep 不同,这意味着您可以指望 *stringp - token 是令牌的长度,这很有用,因为令牌不是空终止的,就像 strsep 一样。如果字符串中没有更多的标记,*stringp - token 将为 0,因此这是您应该测试以结束标记扫描循环的条件。

char* non_nulling_strsep (char** stringp, const char* delims) {
  char* token = *stringp + strspn(*stringp, delims);
  *stringp = token + strcspn(token, delims);
  return token;
}

您可以像这样扫描令牌:

{
  char *end = buffer;
  for (char *token = non_nulling_strsep(&end, " \t\n");
       end - token;
       token = non_nulling_strsep(&end, " \t\n")) {
    printf("Found '%.*s'\n", end - token, token);
  }
}

这是另一个可能的界面,可能效果更好。这个 returns 长度(如果没有更多标记则为 0)并将字符串指针设置为下一个标记的开头(如果没有更多标记则为字符串末尾)。

size_t next_token(char** tokenp, const char* delims) {
  *tokenp += strspn(*tokenp, delims);
  return strcspn(*tokenp, delims);
}

有了这个,你可以像这样循环:

{
  char *token = buffer;
  for (size_t token_len;
       (token_len = next_token(&token, " \t\n"));
       token += token_len) {
    printf("Found '%.*s'\n", token_len, token);
  }
}