strtok函数
头文件:#include <string.h>
函数原型:char * strtok (char *str, const char * delimiters);
参数:str:待分割的字符串(c-string);delimiters:分割符字符串。
该函数用来将字符串分割成一个个片段。参数 str 指向欲分割的字符串,参数 delimiters 则为分割字符串中包含的所有字符。当 strtok() 在参数 s 的字符串中发现参数 delimiters 中包涵的分割字符时,则会将该字符改为\0 字符。 在第一次调用时,strtok() 必需给予参数 s 字符串,往后的调用则将参数 s 设置成 NULL。每次调用成功则返回指向被分割出片段的指针。
需要注意的是,使用该函数进行字符串分割时,会破坏被分解字符串的完整,调用前和调用后的 s 已经不一样了。第一次分割之后,原字符串 str 是分割完成之后的第一个字符串,剩余的字符串存储在一个静态变量中,因此多线程同时访问该静态变量时,则会出现错误。
代码举例:
例 1:将字符串通过’,’分割开,打印子串并判断原字符串(被分割的字符串)是否发生改变
#include <stdio.h>
#include <string.h>
int main() {
char s[] = "ab, ,cd,ef";//注意,第1、2个逗号之间有1个空格
const char* delim = ",";
char* token = NULL;
char* str = strdup(s);
printf("s=%s\n",s);
printf("str=%s\n\n",str);
token = strtok(str, delim);
while (token) {
printf("token=%s\n", token);
token = strtok(NULL, delim);
}
return 0;
}
输出结果:
s=ab, ,cd,ef
str=ab, ,cd,ef
token=ab
token=
token=cd
token=ef
值得注意的是,如果分割的子字符串为空,将跳过该子字符串。而strsep不会跳过空字符串。
#include <stdio.h>
#include <string.h>
int main() {
char s[] = "ab,,cd,ef";//注意,第1、2个逗号之间没有空格
const char* delim = ",";
char* token = NULL;
char* str = strdup(s);
printf("s=%s\n",s);
printf("str=%s\n\n",str);
token = strtok(str, delim);
while (token) {
printf("token=%s\n", token);
token = strtok(NULL, delim);
}
return 0;
}
输出结果:
s=ab,,cd,ef
str=ab,,cd,ef
token=ab
token=cd
token=ef
例 2:MSDN 例子,字符串中含有多个分隔符
#include <string.h>
#include <stdio.h>
char string[] = "A string\tof ,,tokens\nand some more tokens";
char seps[] = " ,\t\n";
char *token;
void main( void )
{
printf( "%s\n\nTokens:\n", string );
token = strtok( string, seps );
while( token != NULL )
{
printf( " %s\n", token );
token = strtok( NULL, seps );
}
}
输出结果:
A string of ,,tokens
and some more tokens
Tokens:
A
string
of
tokens
and
some
more
tokens
结论:strtok 在切割字符串的过程,实际上就是将被分割的字符串的分隔字符替换为‘\0’ 并且返回标记字符串的首地址,直到返回 NULL 结束。
stotok 函数的实现原理
函数代码:
#include<stdio.h>
#include<string.h>
char* myStrtok_origin(char* str_arr,const char* delimiters,char** temp_str)
{
char* b_temp;
if(str_arr == NULL)
{
str_arr =*temp_str;
}
str_arr += strspn(str_arr, delimiters);
if(*str_arr =='\0')
{
return NULL;
}
b_temp = str_arr;
str_arr = strpbrk(str_arr, delimiters);
if(str_arr == NULL)
{
*temp_str = strchr(b_temp,'\0');
}
else
{
*str_arr ='\0';
*temp_str = str_arr +1;
}
return b_temp;
}
char* myStrtok(char* str_arr,const char* delimiters)
{
static char* last;
return myStrtok_origin(str_arr, delimiters,&last);
}
int main(void)
{
char buf[]="hello@boy@this@is@heima";
char*temp_str = NULL;
char*str = myStrtok_origin(buf,"@",&temp_str);
while(str)
{
printf("%s ",str);
str = myStrtok_origin(NULL,"@",&temp_str);
}
char*str1 = myStrtok(buf,"@");
while(str1)
{
printf("%s ",str1);
str1 = myStrtok(NULL,"@");
}
return 0;
}
输出结果:
hello boy this is heima hello
strtok_s 函数
strtok_s 是 Windows 下的一个分割字符串安全函数,其函数原型char *strtok_s(char *strToken, const char *strDelimit, char **buf);
数将剩余的字符串存储在 buf 变量中,而不是静态变量中,从而保证了安全性。
代码示例:
例 1:使用 strtok_s 函数分隔字符串
int main()
{
char str[]="ab,cd,ef";
char *ptr;
printf("before strtok: str=%s\n",str);
printf("begin:\n");
char *pTmp = NULL;
ptr = strtok_s(str,",",&pTmp);
while(ptr != NULL)
{
printf("str=%s\n",str);
printf("ptr=%s\n",ptr);
ptr = strtok_s(NULL,",",&pTmp);
}
return 0;
}
输出结果:
before strtok: str=ab,cd,ef
begin:
str=ab
ptr=ab
str=ab
ptr=cd
str=ab
ptr=ef
strtok_r 函数
strtoks 函数是 linux 下分割字符串的安全函数,函数声明如下:char *strtok_r(char *str, const char *delim, char **saveptr);
(1)该函数也会破坏带分解字符串的完整性,但是其将剩余的字符串保存在 saveptr 变量中,保证了安全性。
(2)在函数 strtok 中剩余字符串是存储在一个静态变量中,因此,多线程在使用该静态变量时引起冲突;而 strtok_r 则使用用户传入的指针为每个用户 saveptr 重新申请变量,因而可以保证线程安全。
(3)strtok_r 函数是 strtok 函数的可重入版本,也即线程安全版本。str 为要分解的字符串,delim 为分隔符字符串。char *saveptr 参数是一个指向 char_ 的指针变量,用来在 strtok_r 内部保存切分时的上下文,以应对连续调用分解相同源字符串。
代码示例:
int main()
{
char str[]="ab,cd,ef";
char *ptr;
char *p;
printf("before strtok: str=%s\n",str);
printf("begin:\n");
ptr = strtok_r(str, ",", &p);
while(ptr != NULL){
printf("str=%s\n",str);
printf("ptr=%s\n",ptr);
ptr = strtok_r(NULL, ",", &p);
}
return 0;
}
输出结果:
原文:https://blog.csdn.net/yishizuofei/article/details/78232946
总结
1. strtok是不可重入的(strtok_r是strtok的可重入版本),strseq是可重入的。
2. strsep和strtok都对修改了src字符串。所以不能使用字符串常量作为分割字符串。
例如:
char* str = "he*#llo*wo";
char* delim = "*#"
char* ret = strtok(str,delim); #错误,str为常量,不可更改
3. strsep和strtok对字符串分割结果不一致。若被分割的字符串有连续的多个分割符出现,strtok会返回NULL,而strsep会返回空字符串;因此,我们若想用strsep分割字符串,必须进行返回值是否为空的检验。
4. 最好使用strsep;避免使用strtok。
最新的Linux内核2.6.29,说明了strtok()已经不再使用,由速度更快的strsep()代替。