C标准库函数strtok使用心得

strtok函数

大家都知道strtok函数可以用来对字符串进行分割,大大满足了我们处理字符串时候的需求,不需要我们自己再去写一些分割函数。

自己在使用过程中也总结了两点需要注意的地方跟大家分享一下:

首先这个函数的原型

char *strtok(char s[], const char *delim);

strtok()用来将字符串分割成一个个片段。

参数s指向欲分割的字符串,参数delim则为分割字符串中包含的所有字符。

当strtok()在参数s的字符串中发现参数delim中包含的分割字符时,则会将该字符改为\0 字符。

在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。

每次调用成功则返回指向被分割出片段的指针。

一般使用的代码是:

1
2
3
4
5
6
7
char* temp1;
char* temp2;
temp1 = strtok(s, delim);
if(temp1 != NULL
{
temp2 = strtok(NULL,delim);
}

当然如果有多个s中有多个delim分隔符的时候,可以用while循环来逐个分割,要注意的是strtok的第一个参数,除了第一次采用s后,之后全部采用NULL。

现在回归正题,从两个参数分别说一下strtok需要注意的地方是:

1. 第一个参数char s[]

这个参数需要注意的是,s字符串所在的地址指向的值必须是可修改的,因为strtok函数内部实现中会去先找到delim的位置,然后将delim位置指向的字符修改为’\0’,然后逐个取出。
所以如果s地址位于常量区、或者堆区,使用这个函数的时候会发生错误。
如下:

1
2
3
4
5
6
7
8
char* s = "aa,bb,cc";
char* temp1;
char* temp2;
temp1 = strtok(s, delim);
if(temp1 != NULL
{
temp2 = strtok(NULL,delim);
}

由于”aa,bb,cc”是位于堆区的,所以指向’,’的指针*p,不能再被*p = ‘\0’。

2. 第二个参数delim

delim分隔符在使用的时候,要注意的是都想当然的以为函数在分割字符串时完整匹配分隔符delim。
比如delim=”ab”,则对于”acdeab”这个字符串,函数提取出的是”acde”。
至少我在第一次使用的时候也是这么认为的。
其实我们都错了,我是在看函数的源代码时才发现这个问题的。
且看下面的例子

1
2
3
4
5
6
7
8
9
10
11
int main(void)
{
char str[] = "acdeab";
char *p = NULL;
int in = 0;
p = strtok(str, "ab");
while(p != NULL){
printf("the character is :%s\n",p);
p = strtok(NULL,"ab");
}
}

输出的结果为:

the character is :cde

第一次调用之后的结果竟然是”cde”,而非我们所想的结果。这是为什么呢?

我们回到GNU C Library中对strtok的功能定义:

“Parse S into tokens separated by characters in DELIM”。

也就是说包含在delim中的字符均可以作为分隔符,而非严格匹配。

可以把delim理解为分隔符的集合。这一点是非常重要的。

当然,我们在分解字符串的时候,很少使用多个分隔符。这也导致,很多人在写例子的时候只讨论了一个分隔符的情况。有更多的人在看例子的时候也就错误的认识了delim的作用。

所以大家使用过程中要多注意了。