strtok/strtok_r 中途退出解析
strtok/strtok_r quit parsing in the middle
strtok_r
在解析时将空字符放入输入字符串的不同位置。 strtok_r
returns 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; */
}
}
}
strtok() 和 strtok_r() 是糟糕的函数:
- 他们修改了输入字符串
- 它们将连续的分隔符视为一个分隔符,这在跳过空格时可能是有意的,但在解析 .CSV(或制表符分隔的)输入时并非如此。
最好完全避免使用 strtok() 和 strtok_r(),而使用 strspn() 和 strcspn()。下面的函数就是这样做的。 return值类似于snprintf():找到的token中的字符数(不包括终止NUL字节)
- 如果没有令牌#n: '\0' 被写入缓冲区并且 0 被 returned
- 如果缓冲区对于找到的令牌加上终止 NUL 字节来说太小,'\0' 将写入缓冲区并且令牌长度为 returned
- 缓冲区是否足够大,token + '\0'写入其中,strlen(token) returned.
#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);
strtok_r
在解析时将空字符放入输入字符串的不同位置。 strtok_r
returns 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; */
}
}
}
strtok() 和 strtok_r() 是糟糕的函数:
- 他们修改了输入字符串
- 它们将连续的分隔符视为一个分隔符,这在跳过空格时可能是有意的,但在解析 .CSV(或制表符分隔的)输入时并非如此。
最好完全避免使用 strtok() 和 strtok_r(),而使用 strspn() 和 strcspn()。下面的函数就是这样做的。 return值类似于snprintf():找到的token中的字符数(不包括终止NUL字节)
- 如果没有令牌#n: '\0' 被写入缓冲区并且 0 被 returned
- 如果缓冲区对于找到的令牌加上终止 NUL 字节来说太小,'\0' 将写入缓冲区并且令牌长度为 returned
- 缓冲区是否足够大,token + '\0'写入其中,strlen(token) returned.
#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);