strtok
是 C 标准库中的字符串分割函数,用于将一个字符串拆分成多个部分(token),以某些字符(称为分隔符)为界限。
函数原型
char *strtok(char *str, const char *delim);
-
参数:
str
:待分割的字符串。如果是第一次调用,传入要分割的字符串;之后的调用需传入NULL
,以继续上一次的分割。delim
:字符串,包含所有分隔符的字符集合。例如," "
(空格)或"/"(斜杠)
。
-
返回值:
- 返回指向字符串中 当前部分 的指针。
- 如果没有更多部分可返回,返回
NULL
。
用法规则
- 初次调用时,传入字符串
str
,函数会从str
中找到第一个部分。 - 函数会用
'\0'
替换找到的分隔符(破坏原字符串)。 - 后续调用时,传入
NULL
,函数会继续从上次结束的位置查找下一部分。 - 不能在多线程环境中使用,因为
strtok
使用的是静态变量保存状态。
示例
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "C programming is fun";
const char *delim = " "; // 用空格作为分隔符
char *token;
// 初次调用 strtok
token = strtok(str, delim);
while (token != NULL) {
printf("%s\n", token); // 打印分割的每一部分
token = strtok(NULL, delim); // 后续调用
}
return 0;
}
输出:
C
programming
is
fun
注意事项
-
原字符串被修改:
strtok
会在分隔符处插入'\0'
,因此原字符串内容会被破坏。如果需要保留原字符串,请先复制到一个新字符串再操作。 -
不能并发使用:由于
strtok
使用内部静态变量保存状态,它在多线程程序中不安全。多线程环境下,请使用strtok_r
。
分隔符的处理
分隔符 delim
可以包含多个字符,strtok
会将所有出现在 delim
中的字符视为分隔符。例如:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "apple;orange,banana|grape";
const char *delim = ";,|"; // 多个分隔符
char *token;
token = strtok(str, delim);
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, delim);
}
return 0;
}
输出:
apple
orange
banana
grape
如何在多线程中使用?
在多线程环境下,使用 strtok_r
(线程安全版本)。函数原型如下:
char *strtok_r(char *str, const char *delim, char **saveptr);
参数说明
str
:首次调用传入要分割的字符串,后续调用传入NULL
。delim
:分隔符集合。saveptr
:保存分割状态的指针变量,需由调用者提供。
示例
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "apple;orange,banana|grape";
const char *delim = ";,|";
char *token;
char *saveptr; // 保存状态
token = strtok_r(str, delim, &saveptr);
while (token != NULL) {
printf("%s\n", token);
token = strtok_r(NULL, delim, &saveptr);
}
return 0;
}
输出:
apple
orange
banana
grape
为什么需要传入 NULL
?
strtok
函数内部使用了一个 静态指针 来记录分割状态。这个静态指针会指向原字符串中上一次分割结束的位置。在首次调用时,strtok
初始化这个指针为传入的字符串地址。在后续调用时,通过传入 NULL
,strtok
会继续从这个内部静态指针的位置接着分割。
静态指针的工作原理
-
初次调用:
token = strtok(str, delim);
- 参数
str
是待分割字符串。 strtok
将分隔符替换为'\0'
,并返回找到的第一个分割部分,同时保存分割结束的位置到静态指针中。
- 参数
-
后续调用:
token = strtok(NULL, delim);
- 参数
NULL
告诉strtok
使用保存的静态指针,从上次分割结束的位置继续分割。 - 每次分割后,
strtok
会更新静态指针的位置。
- 参数
示例分析
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "C,programming,is,fun";
const char *delim = ",";
char *token;
// 初次调用 strtok
token = strtok(str, delim);
printf("First token: %s\n", token); // 输出 "C"
// 后续调用 strtok
token = strtok(NULL, delim);
printf("Second token: %s\n", token); // 输出 "programming"
token = strtok(NULL, delim);
printf("Third token: %s\n", token); // 输出 "is"
token = strtok(NULL, delim);
printf("Fourth token: %s\n", token); // 输出 "fun"
token = strtok(NULL, delim);
printf("Final token: %s\n", token); // 输出 "(null)",分割完成
return 0;
}
输出:
First token: C
Second token: programming
Third token: is
Fourth token: fun
Final token: (null)
内部指针变化:
- 初次调用:
str
是"C,programming,is,fun"
。- 静态指针保存分割后的位置
"programming,is,fun"
。
- 第二次调用:
- 使用静态指针继续分割。
- 静态指针更新为
"is,fun"
。
- 第三次调用:
- 静态指针更新为
"fun"
。
- 静态指针更新为
- 第四次调用:
- 静态指针更新为
NULL
。
- 静态指针更新为
静态指针的局限性
1. 线程不安全
strtok
的静态指针是全局共享的,多个线程同时调用 strtok
会导致状态混乱。
解决方案:使用线程安全的 strtok_r
函数,它将状态保存到用户提供的指针(而非静态变量)中。
2. 不能同时分割多个字符串
因为 strtok
的静态指针只能记录一个字符串的分割状态。如果需要同时分割多个字符串,应使用其他方法(如 strtok_r
或自定义逻辑)。
总结
strtok
用于字符串分割,但会修改原字符串,并且线程不安全。- 多线程环境下,推荐使用
strtok_r
。 - 分割的字符串存储在原字符串中,指针返回的是分割后的部分,原字符串会被破坏。