字符串分割函数是非常重要的一个函数,就连如何使用也需要大家花上一段时间。所以这里首先说一下strtok的处理方式:
strtok会首先过滤掉所有的所有的属于分割字符串集合的字符,然后进行扫描并将之后碰到的属于分割字符串集合中的字符使用空结束符'/0'来替代,这样就可以直接使用该函数返回的直接读取第一个token(因为它读到'/0'就结束了,很好!!!!)。之后获取剩下的token的时候直接给strtok传递NULL作为第一个参数,从而不断取得下一个token。
如果大家使用的比较多,或者通过我上面的描述一定会很疑惑,代码是怎么实现的?为什么传递一个NULL就可以获取下一个token了?返回的指针值一定又移到了下一个地方(上一个token后的一个位置),如果每次都从头开始扫描,怎么知道在何处停下?因为程序根本不知道当前要处理的是第几个token啊,我只是简单的传递了一个NULL值而已。
原来,这里使用了一个静态变量,通过对该静态变量的操作来保证对指针的修改(基于上一个token的记录);除此之外,strtok另一个不太好的地方是它修改了原字符串,将对应位置替换为了'/0',这也是为什么这个函数的第一个参数不能是const char * 的原因。
下面是strtok实现的代码:
- static char* ts=NULL;
- char* strtok(char* s,const char* ct){
- assert(s!=NULL&&ct!=NULL);
- s=ts;//last position
- if(!s&&*s==0)
- return NULL;
- char* t;
- do{
- for(t=ct,ts=NULL;*t;t++){
- if(*t==*s){
- if(*(ts=++s)) break;
- return ts=NULL;
- }
- }
- }while(ts);
- for(ts=s;*ts;ts++)
- for(t=ct;*t;t++)
- if(*ts==*t){
- *ts++='/0';
- return s;
- }
- ts=NULL;
- return s;
- }
请注意仔细学习上面的代码。下面一部分内容很重要:
因为strtok函数使用了全局的静态变量,因此该函数是不可重入的,而且也不是线程安全的,因此在同一时间内多次使用该函数和多个线程使用该函数时会出现问题。
由此产生了该函数的可重入版本:strrtok,多出的一个r意即reentrant(可重入),这两个函数的优缺点以及实例效果分析请参考:http://blog.chinaunix.net/u2/66402/showart_1168731.html ,里面有详细的比较。
而strrtok又是如何实现其可重入的呢,它只是多出了一个参数而已,strrtok的函数原型为:
char *strtok_r(char *s, const char *delim, char **ptrptr);
原来,后来的ptrptr是用来传递一个指针的引用,因此在使用该函数前需要先在外面定义一个指针变量,这个变量刚好可以替代了strtok中的静态变量的作用,因此不同的使用过程定义不同的一个指针变量传递进去即可,从而很好的实现了可重入性:维护自己的变量。
下面是strrtok的一份源码,供参考:
- char * strtok_r(char *s1, const char *s2, char **lasts)
- {
- char *ret;
- if (s1 == NULL)
- s1 = *lasts;
- while(*s1 && strchr(s2, *s1))//去掉前置分隔符
- ++s1;
- if(*s1 == '/0')
- return NULL;
- ret = s1;
- while(*s1 && !strchr(s2, *s1))
- ++s1;
- if(*s1)
- *s1++ = '/0';
- *lasts = s1;
- return ret;
- }
有关函数的可重入及线程安全的概念请参考:http://liuaigui.blog.sohu.com/86494742.html