strtok
strtok是C标准库<string.h>中用于按分隔符把字符串切分成若干标记(token)的库函数,其名称源自 string token 的缩写。调用者首次传入待处理字符串指针,之后以空指针继续调用,即可逐个取得被分隔出的子串。该函数会直接改写原字符串,并依赖库内部保存的检索位置,因此不可重入、也不线程安全,在并发或多层嵌套切分的场合通常改用 strtok_r 等替代接口。
定义
strtok是C语言标准库头文件<string.h>中声明的一个字符串处理函数,原型为 char *strtok(char *str, const char *delim),作用是按照 delim 给出的分隔字符集合,把一个以空字符结尾的字节串切分为连续的标记[1]。分隔字符集合可以只含 1 个字符,也可以同时包含多个字符,函数会把其中任意一个字符都视为边界[2]。
函数的返回值是指向当前标记首字节的指针;当字符串中已经不存在可分割的内容时,返回空指针[3]。按照标准描述,strtok 切出的标记一定是非空字符串,长度为零的片段不会作为结果返回[4]。
原理
strtok 采用「首次传串、后续传空」的调用约定。序列中的第一次调用把待处理字符串地址作为第 1 个实参;此后为取得同一字符串中的其余标记,第 1 个实参须改为空指针,由库内部记录上次扫描停止的位置[2]。每次调用都先在当前位置跳过属于分隔集合的字符,找到第一个非分隔字符作为标记起点,再向后寻找第一个分隔字符[5]。
找到分隔字符后,函数把它就地改写成空字符 '\0',使当前标记在此结束,并保存其后续字符的位置供下次调用使用;若一直到串尾都未遇到分隔字符,则当前标记延续到字符串末尾,下一次调用返回空指针[3]。由此可以推出两个附带效果:连续出现的多个分隔字符被当作单个分隔处理,串首和串尾的分隔字符被忽略;例如以「;,」为分隔集合处理「aaa;;bbb,」,依次得到的是 aaa 和 bbb[6]。
分隔集合在每次调用时可以不同,因此后续标记的切分规则甚至可以依赖前一个标记的内容,这一点与多数分词接口不同[7]。整个过程可概括为:定位标记起点、定位标记终点、写入终止符、保存续扫位置,四步循环执行,直到返回空指针。
发展历程
strtok 的接口最早来自 UNIX 系统的字符串处理惯例,POSIX 文档记载其源自 SVID 的第 1 版,并被 BSD 4.3 等系统沿用[2]。1989 年发布的 C89(C90)标准把 strtok 收入标准库,此后 C99、C11、C17 与 C23 各版标准均在 <string.h> 中保留该函数,其原型在 C99 起增加了 restrict 限定[1]。
为解决不可重入问题,POSIX 线程扩展(1003.1c-1995)引入了 strtok_r,由调用者自行提供保存续扫位置的指针,从而支持多线程与嵌套调用[2]。C11 标准则在附录 K 的边界检查接口中给出 strtok_s,要求额外传入剩余长度并做运行期约束检查[1]。需要注意的是,strtok_s 属于可选的支持部分,只有实现定义了相应宏、且使用者在包含头文件前做了设置时才保证可用,这也使部分项目在可移植性权衡后仍选择 strtok_r[8]。
应用
strtok 的典型用途是把带固定分隔符的记录拆成字段,例如按冒号切分环境变量 PATH 中的各个目录,按逗号切分数据文件中的列,或按空白切分命令行输入[9]。由于分隔集合可逐次变化,它也能处理结构较松散的文本,例如先按分号切出语句、再按等号切出键值对。
在教学与小型工具程序中,strtok 常与 while 循环搭配使用:先以原串调用取得第一个标记,再以空指针反复调用,直至返回空指针为止[5]。在需要并发处理多个字符串的环境里,一般改用 strtok_r,用不同的保存指针对不同字符串并行扫描[3]。
局限
最突出的限制是破坏性:strtok 会向原字符串写入终止符,把分隔字符本身覆盖掉,因此不能作用于字符串常量,也不能指望原串在调用后保持完整;若需保留原文,应先复制到可写缓冲区再传入[9]。同时,分隔字符的原始身份在改写后不复存在,无法据此还原原始格式[3]。
其次是不可重入与线程安全问题。strtok 借助库内静态状态记录扫描位置,多线程同时处理不同字符串时会相互干扰,同一线程内的嵌套切分也无法实现,例如在外层切分尚未完成时对内层标记再次切分[8]。C 标准并未要求该函数避免数据竞争,POSIX 亦指明其不必可重入;对应的处理办法是使用 strtok_r 或 strtok_s[10]。
第三是语义上的损失:连续分隔符会被折叠,空字段无法取得,串首串尾的分隔符被直接忽略,这对需要区分空列的格式(如某些表格数据)并不合适[4]。此外,当第 1 个参数或分隔集合不是指向以空字符结尾的字节串时,行为未定义,输入校验责任完全落在调用方[7]。
参见
参考资料
- strtok, strtok_s [引用日期2026-10-29]
- strtok [引用日期2026-10-29]
- strtok [引用日期2026-10-29]
- strtok [引用日期2026-10-29]
- strtok.3.fr [引用日期2026-10-29]
- strtok.3.pl [引用日期2026-10-29]
- strtok, strtok_s [引用日期2026-10-29]
- [PATCH 00/21] replace strtok with strtok_r [引用日期2026-10-29]
- 页面历史记录 - The C99 \[[ISO/IEC 9899:1999|AA [引用日期2026-10-29]
- diffpagesbyversion [引用日期2026-10-29]