1. 今天看了看strtok函数,特意找了下Linux内核2.0.1版本的代码,因为在更高版本(至少2.6)已经使用strsep替换了该函数.
函数原型:
char * strtok(char * s,const char * ct)
使用第二个参数ct中的分隔符字符串,分割第一个参数s,ct参数的分隔符可以是任意字符,可以是单个字符的分隔符,也可以是字符串形式的分隔符如:"!,;'/"等,都可以作为分隔符。例如:
s="abc,def,123;456!/aaa"
ct=",;!/"s将被分割为为:abc def 123 456 aaa
测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char s[] = "abc,def,123;456!/aaa";
char delim[] = " ,;!/";
char *p = NULL;
for(p = strtok(s, delim); p != NULL; p = strtok(NULL, delim))
{
printf("%s ", p);
}
printf("\n");
return 0;
}
[ljq@ycy algorithm]$ gcc strtok.c -o strtok
[ljq@ycy algorithm]$ ./strtok
abc def 123 456 aaa
2. strtok代码分析,以下代码摘自Linux2.01.版本:
char * strtok(char * s,const char * ct)
{
char *sbegin, *send;
sbegin = s ? s : ___strtok;
if (!sbegin) {
return NULL;
}
sbegin += strspn(sbegin,ct);
if (*sbegin == '\0') {
___strtok = NULL;
return( NULL );
}
send = strpbrk( sbegin, ct);
if (send && *send != '\0')
*send++ = '\0';
___strtok = send;
return (sbegin);
}
先说下strtok的整体思想也就是关键的几个步骤:
a。首先strtok将数据保存在全局变量__strtok中,因此不是线程安全的也即不可重入。
b。strtok查找分隔符字符串时,跳过连续的分隔符,这样可以忽略连续分隔符之间的空串,连续分隔符是从源字符串开始位置计算,个数通过函数strspn计算得出。
size_t strspn(const char *s, const char *accept),计算字符串 str 中连续有几个字符都属于字符串 accept。
例如:";,/!ABC!/DEF",前四个字符都是分隔符且是连续的,那么strspn函数返回4,有效数据跳过前4个分隔符就从字符A开始,直到下一分隔符。
c。调用函数strpbrk,即源字符串中的字符如果与分隔符字符串中任意字符相同,就返回指向源字符串中该字符的指针,即找到了分隔符,返回该数据的指针。
char * strpbrk(const char * cs,const char * ct),比较字符串str1和str2中是否有相同的字符,如果有,则返回该字符在str1中的位置的指针。
例如:";,/!ABC!/DEF",跳过4个分隔符后,从A开始直到遇到分隔符感叹号(!) 那么,就返回指向数据起始位置的指针,该指针指向字符A。
细节分析:
a。 变量__strtok是全局变量,定义在string.c文件中:char * ___strtok = NULL; 在头文件linux/string.h中进行外部声明,只要使用该变量的c文件包含该头文件即可;
b。全局变量__strtok保存了剩余未做分隔的字符串的起始地址,每次调用strtok函数,都从全局变量__strtok指向的地址开始查找参数中的分隔符字符串,找到之后__strtok指向本次分隔符的下一位置(有效数据或者结尾符\0,又或者是分隔符如果有连续分隔符的话)。
c。对于字符串 "abc,def,123;456!/aaa"; 第一次调用strtok之后,分隔符逗号(,)被设置为\0,__strtok设置为指向第一个分隔符逗号之后的数据即__strtok指向字符d,返回指向字符a的指针;第二次调用strtok时,从__strtok指向的字符d开始查找delim中的分隔符,找到第二个逗号时,与第一次操作一样,分隔符逗号被设置为\0,并从新设置__strtok指向字符1,返回指向字符d的指针,后续一直如此循环。
d。如果源字符串包含连续分隔符,则调用函数strspn计算出连续相同的分隔符字符后会跳过这些分隔符。
附上函数strspn的测试示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int ret = 0;
char s1[] = "abc,;!/}defgh,,,**#123,456";
char s2[] = ";,/#}*abc,;!/}defgh,,,**#123,456";
char delim[] = ",;!/}#*";
ret = strspn(s1, delim);
printf("ret1:%d\n", ret);
ret = strspn(s2, delim);
printf("ret2:%d\n", ret);
return 0;
}
结果如下:
[ljq@ycy algorithm]$ gcc strspn.c -o strspn
[ljq@ycy algorithm]$ ./strspn
ret1:0
ret2:6
函数是从源字符串开始位置匹配分隔符,只有开始的连续才起作用,开始有1个或者多个则返回匹配到的分隔符个数,如果开始位置一个分隔符都没有,则返回0,如果有1个则返回1个,上例中返回6个。