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:将字符串通过’,’分割开,打印子串并判断原字符串(被分割的字符串)是否发生改变

  1. #include <stdio.h>
  2. #include <string.h>
  3. int main() {
  4. char s[] = "ab, ,cd,ef";//注意,第1、2个逗号之间有1个空格
  5. const char* delim = ",";
  6. char* token = NULL;
  7. char* str = strdup(s);
  8. printf("s=%s\n",s);
  9. printf("str=%s\n\n",str);
  10. token = strtok(str, delim);
  11. while (token) {
  12. printf("token=%s\n", token);
  13. token = strtok(NULL, delim);
  14. }
  15. return 0;
  16. }

输出结果
s=ab, ,cd,ef
str=ab, ,cd,ef

token=ab
token=
token=cd
token=ef

值得注意的是,如果分割的子字符串为空,将跳过该子字符串。而strsep不会跳过空字符串。

  1. #include <stdio.h>
  2. #include <string.h>
  3. int main() {
  4. char s[] = "ab,,cd,ef";//注意,第1、2个逗号之间没有空格
  5. const char* delim = ",";
  6. char* token = NULL;
  7. char* str = strdup(s);
  8. printf("s=%s\n",s);
  9. printf("str=%s\n\n",str);
  10. token = strtok(str, delim);
  11. while (token) {
  12. printf("token=%s\n", token);
  13. token = strtok(NULL, delim);
  14. }
  15. return 0;
  16. }

输出结果:
s=ab,,cd,ef
str=ab,,cd,ef

token=ab
token=cd
token=ef

例 2:MSDN 例子,字符串中含有多个分隔符

  1. #include <string.h>
  2. #include <stdio.h>
  3. char string[] = "A string\tof ,,tokens\nand some more tokens";
  4. char seps[] = " ,\t\n";
  5. char *token;
  6. void main( void )
  7. {
  8. printf( "%s\n\nTokens:\n", string );
  9. token = strtok( string, seps );
  10. while( token != NULL )
  11. {
  12. printf( " %s\n", token );
  13. token = strtok( NULL, seps );
  14. }
  15. }

输出结果
A string of ,,tokens
and some more tokens

Tokens:
A
string
of
tokens
and
some
more
tokens

结论:strtok 在切割字符串的过程,实际上就是将被分割的字符串的分隔字符替换为‘\0’ 并且返回标记字符串的首地址,直到返回 NULL 结束。

stotok 函数的实现原理
函数代码

  1. #include<stdio.h>
  2. #include<string.h>
  3. char* myStrtok_origin(char* str_arr,const char* delimiters,char** temp_str)
  4. {
  5. char* b_temp;
  6. if(str_arr == NULL)
  7. {
  8. str_arr =*temp_str;
  9. }
  10. str_arr += strspn(str_arr, delimiters);
  11. if(*str_arr =='\0')
  12. {
  13. return NULL;
  14. }
  15. b_temp = str_arr;
  16. str_arr = strpbrk(str_arr, delimiters);
  17. if(str_arr == NULL)
  18. {
  19. *temp_str = strchr(b_temp,'\0');
  20. }
  21. else
  22. {
  23. *str_arr ='\0';
  24. *temp_str = str_arr +1;
  25. }
  26. return b_temp;
  27. }
  28. char* myStrtok(char* str_arr,const char* delimiters)
  29. {
  30. static char* last;
  31. return myStrtok_origin(str_arr, delimiters,&last);
  32. }
  33. int main(void)
  34. {
  35. char buf[]="hello@boy@this@is@heima";
  36. char*temp_str = NULL;
  37. char*str = myStrtok_origin(buf,"@",&temp_str);
  38. while(str)
  39. {
  40. printf("%s ",str);
  41. str = myStrtok_origin(NULL,"@",&temp_str);
  42. }
  43. char*str1 = myStrtok(buf,"@");
  44. while(str1)
  45. {
  46. printf("%s ",str1);
  47. str1 = myStrtok(NULL,"@");
  48. }
  49. return 0;
  50. }

输出结果:
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 函数分隔字符串

  1. int main()
  2. {
  3. char str[]="ab,cd,ef";
  4. char *ptr;
  5. printf("before strtok: str=%s\n",str);
  6. printf("begin:\n");
  7. char *pTmp = NULL;
  8. ptr = strtok_s(str,",",&pTmp);
  9. while(ptr != NULL)
  10. {
  11. printf("str=%s\n",str);
  12. printf("ptr=%s\n",ptr);
  13. ptr = strtok_s(NULL,",",&pTmp);
  14. }
  15. return 0;
  16. }

输出结果
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 内部保存切分时的上下文,以应对连续调用分解相同源字符串。

代码示例

  1. int main()
  2. {
  3. char str[]="ab,cd,ef";
  4. char *ptr;
  5. char *p;
  6. printf("before strtok: str=%s\n",str);
  7. printf("begin:\n");
  8. ptr = strtok_r(str, ",", &p);
  9. while(ptr != NULL){
  10. printf("str=%s\n",str);
  11. printf("ptr=%s\n",ptr);
  12. ptr = strtok_r(NULL, ",", &p);
  13. }
  14. return 0;
  15. }

输出结果
[转]字符串切割函数strtok、strtok_s、strtok_r的区别 - 图1
原文:https://blog.csdn.net/yishizuofei/article/details/78232946

总结

  1. strtok是不可重入的(strtok_r是strtok的可重入版本),strseq是可重入的。
  2. strsep和strtok都对修改了src字符串。所以不能使用字符串常量作为分割字符串。 
例如:

  1. char* str = "he*#llo*wo";
  2. char* delim = "*#"
  3. char* ret = strtok(str,delim); #错误,str为常量,不可更改

  3. strsep和strtok对字符串分割结果不一致。若被分割的字符串有连续的多个分割符出现,strtok会返回NULL,而strsep会返回空字符串;因此,我们若想用strsep分割字符串,必须进行返回值是否为空的检验。
  4. 最好使用strsep;避免使用strtok。

  1. 最新的Linux内核2.6.29,说明了strtok()已经不再使用,由速度更快的strsep()代替。

原文链接:https://blog.csdn.net/z_ryan/article/details/79252381