关于__strtok_r函数中strspn与strcspn代码逻辑的技术咨询
关于__strtok_r中两行代码的解析及自定义分隔符保留版strtok的思路
首先明确两个核心函数的行为,这是理解代码的关键:
strspn(s, delim):返回s开头连续属于delim集合的字符总数,简单说就是跳过开头所有分隔符的步数。strcspn(s, delim):返回s开头连续不属于delim集合的字符总数,也就是找到第一个分隔符前的字符数。
1. s += strspn(s, delim); 的作用
这行代码的核心是跳过当前字符串开头所有的分隔符,把指针定位到下一个有效token的起始位置。
举你的例子:s = "Abc,dsf.eerr",delim = " ,.\n"
因为s开头的A、b、c都不属于分隔符集合,strspn(s, delim)返回0,所以s仍然指向A,而不是你以为的d。只有当s开头是分隔符时,比如s = ",dsf.eerr",strspn会返回1,s会跳到d的位置。
2. end = s + strcspn(s, delim); 的作用
这行是定位当前token的结束位置:strcspn会计算从s开始到第一个分隔符的字符数,s + 这个数值就指向第一个分隔符的地址(如果字符串末尾没有分隔符,则指向字符串结束的\0)。
还是你的例子:s指向A,strcspn(s, delim)返回3(A、b、c都不属于分隔符,下一个字符是,),所以end指向,的位置。原__strtok_r会在这里把*end设为\0,截断出当前token("Abc"),然后把下一次的起始位置设为end+1(指向d)。
3. 实现保留分隔符的strtok思路
如果你要保留分隔符并返回,需要调整原逻辑,把分隔符也作为结果的一部分返回(比如通过输出参数,或者返回包含token和分隔符的结构体),核心步骤可以是:
- 先处理开头的分隔符(如果需要保留连续分隔符,比如多个逗号,也可以作为单独的分隔符返回)
- 用
strcspn找到第一个分隔符的位置:- 从当前
s开始,token_len = strcspn(s, delim),所以token是s到s+token_len-1 - 如果
s[token_len]不是\0,则分隔符是s[token_len],下一次的起始位置设为s+token_len+1 - 如果
s[token_len]是\0,则没有分隔符,下一次返回NULL
- 从当前
- 可以设计函数返回token的指针,同时通过指针参数返回分隔符(比如
char *my_strtok(char *s, const char *delim, char *out_delim))
举个简化的逻辑示例:
char *my_strtok(char *s, const char *delim, char *out_delim) { static char *next = NULL; if (s != NULL) next = s; if (next == NULL || *next == '\0') return NULL; // 跳过开头分隔符(如果不需要保留开头分隔符,保留这行;如果要保留,注释掉并先处理分隔符) next += strspn(next, delim); if (*next == '\0') return NULL; char *token_start = next; size_t token_len = strcspn(next, delim); next += token_len; // 提取分隔符 if (*next != '\0') { *out_delim = *next; next++; // 跳过分隔符,准备下一次调用 } else { *out_delim = '\0'; } // 不需要截断token,因为要保留原字符串?或者根据需求决定是否截断 // 如果需要返回独立的token,可以分配内存复制,否则直接返回token_start return token_start; }
内容的提问来源于stack exchange,提问作者DirtyV
相关产品推荐
相关产品推荐

