strtok 和strtok_r 函数使用

strtok这个函数我们再熟悉不过了,因为我们要经常要和字符打交道,不可避免的要分割字符串连接字符串。那么我今天看一下分割字符串。

strtok

函数原型char* strtok(char *str,const char *delimiters);

来分析一下这个分割字符串函数,这个函数是在传入的字符数组里进行调整,它并没有生成新的字符数组。
第一个参数可以是字符数组或者NULL,第一次切割传要切割的字符,第二次要在原来字符数组上继续切割就传NULL;

第二个参数传入的是切割符,这个参数看起来简单其实是比第一个还复杂,它传入的是一个字符串,而不是一个字符。
我们知道切割符是一个字符这个很好理解,但是传入一个字符串那函数是怎么进行切割的呢?


my_strtok函数的实现

#include <stdio.h>
#include <string.h>

char *my_strtok(char *buff, char* delimit)
{
 static char *p = NULL;                       //定义一个静态的字符指针  p
 if(buff == NULL && (buff = p) == NULL)    //这个if语句特别重要,解决第一个参数传入的问题
 {                                       //第一个参数是buff不为NULL,if第一条buff == NULL就为非
  return NULL;                  //不执行后面的赋值语句和判断语句,
                                          //如果为空就把buff 赋值成 p
 }
 char *t = buff;                //这个是遍历buff字符串用的
 char *s = NULL;           //这个是遍历delimit分割符用的
            
 while(*t != '\0' )         //第一层遍历buff
 {
  s = delimit;               //把分割符号的地址赋给s
  while( *s != '\0')         //遍历分割符里面的元素
  {
   if(*s == *t)               //如果分割符里面的字符命中了
   {
    p = t+1;                  //把t+1的地址赋给静态指针  
    if( t == buff)             //如果刚开始就命中了分割符
    {
     buff = p;                 //buff 等于t+1
                    //把刚刚在buff中发现的命中分割符给置成\0
      break;                //跳出这层循环,因为现在一块都没切出来,不break,会被下面的语句return出去
    }
    *t = '\0';          
    return buff;
   }
            s++;
  }
        t++;
 } 
 
 if(buff != NULL)
 {
	 return buff;
	 p = NULL;
 }
  return NULL;
}

strtok的使用(联系下面的函数自己理解下)

我们举个例子,

我们要分割一个 char buff[128] = "ab,cd,ef,ghj";
以 “,”为分割符
strtok(buff,",");

第一个形参是buff

第二个是形参是char *delimit
char *tmp 遍历buff数组的指针
char *s  保存着分割符delinit

static char *p 记录每次分割后的第一位字符 

第一次分割。第一个参数我们传入buff,也就是数组的首地址,从a开始分割。遍历buff指针char *p命中分割符后,静态的指针指向这个分割符后面一个字符,并把‘,’置成‘\0’,返回压入的第一个形参地址。


这个时候的buff[128] 变成 "ab\0cd,efghj;

第二次分割时候。我们如果第一个参数接着传入buff,那么就搞笑了。因为我们第一次把第一个‘,’字符置\0。
显然遍历buff指针还跑一遍发现找不到分割符,碰到/0结束了。
所以我们非第一次切割的,记住传入的是NULL,这个是用来区分是不是第一次分割同一个字符串的。我们如果传入的是NULL,那么就是从函数static char*p继续。

这么来说要想分割出ab cd ef ghj 这四个字符串

char *Arg[5] = {0};   //定义一个指针数组每个格子保存分界符的下一位
char *p = strtok(buff,",");    //第一次切割所以传进去的是数组首地址
int i = 0;           
while(p!=NULL)
{
        Arg[i++] = p;              //利用一个指针数组保存每次切割后的第一位字符
        p = strtok(NULL,",");       //非第一次切割传递一个NULL即可
}
 

这样就保存了,每个分割好的字符的首地址了。
而且分割符都被换成\0,
buff[128] = "ab\0cd\0ef\0ghj
Arg 分别保存了  a c e g四个字符的地址指针
当输出时候,就可以输出分割好的字符串

使用strtok注意事项

1)对同一个字符数组切割,第一个形参使用时候记得非第一次次分割,传的是NULL。 

2)还有就是它会对源字符串修改,如果源字符串还要继续使用的话,先利用strcpy一个零时字符数组,在实现字符分割。

3)压入第一个形参是传字符数组,不能传const char *类型,这样是字符串常量不能进行修改。

4 )我们传入的第二个形参是字符串,不是字符,意味着是“ ” ,是双引号,不是单引号‘ ’哪怕只有一个字符


5 )在使用线程中,使用strtok是不安全的,因为他们共享全局变量,strtok的内部静态指针会被所有线程共享,所以才
引出了我们strtok_r这个函数,我们下面会去讲解下。



strtok_r

我们既然已经实现了分割字符串函数strtok,还要引出这个strtok_r呢?
其实strtok 这个函数是有缺陷的,我们通过一个例子来演示一下


执行结果

我们简单的分析一下把

就是strtok里面那个static 静态的全局指针 p,是所有线程共享的,那么就会出现覆盖的问题,这里是主线程先执行,第一次切割的是buff,然后p记录了下次切割的第一位字符,然而线程fun里的开始切割buff1了,切割后,把主线程在p记录的地址覆盖了。于是出现了主线程帮fun这个线程一起切割buff1的情况。
那么就出现了线程不安全的问题。于是引出了strtok_r函数这个是多了个参数。char **m,第三个参数传入的是指针的地址

每个线程的栈是私有的,所以呢就可以在栈上定义个char *m = NULL ,去代替了static char* p。把共有的变成了私有的。这就是strtok 和 strtok_r 的区别。


已标记关键词 清除标记
【为什么还需要学习C++?】 你是否接触很多语言,但从来没有了解过编程语言的本质? 你是否想成为一名资深开发人员,想开发别人做不了的高性能程序? 你是否经常想要窥探大型企业级开发工程的思路,但苦于没有基础只能望洋兴叹?   那么C++就是你个人能力提升,职业之路进阶的不二之选。 【课程特色】 1.课程共19大章节,239课时内容,涵盖数据结构、函数、类、指针、标准库全部知识体系。 2.带你从知识与思想的层面从0构建C++知识框架,分析大型项目实践思路,为你打下坚实的基础。 3.李宁老师结合4大国外顶级C++著作的精华为大家推出的《征服C++11》课程。 【学完后我将达到什么水平?】 1.对C++的各个知识能够熟练配置、开发、部署; 2.吊打一切关于C++的笔试面试题; 3.面向物联网的“嵌入式”和面向大型化的“分布式”开发,掌握职业钥匙,把握行业先机。 【面向人群】 1.希望一站式快速入门的C++初学者; 2.希望快速学习 C++、掌握编程要义、修炼内功的开发者; 3.有志于挑战更高级的开发项目,成为资深开发的工程师。 【课程设计】 本课程包含3大模块 基础篇 本篇主要讲解c++的基础概念,包含数据类型、运算符等基本语法,数组、指针、字符串等基本词法,循环、函数、类等基本句法等。 进阶篇 本篇主要讲解编程中常用的一些技能,包含类的高级技术、类的继承、编译链接和命名空间等。 提升篇: 本篇可以帮助学员更加高效的进行c++开发,其中包含类型转换、文件操作、异常处理、代码重用等内容。
©️2020 CSDN 皮肤主题: 鲸 设计师:meimeiellie 返回首页