前言

strtok 函数的作用是比较独特的,可以用来对字符串进行分割,对于我们获取命令或者数据集合后的数据处理是不可或缺的一步。但是详细介绍 strtok 的博文很少,而且linux手册的介绍也比较简单,这边写篇博文记录一下学习到的这个函数的用法。


一、strtok函数是什么?

函数定义如下:

strtok()函数,将字符串分解为一组字符串
声明:
char *strtok(char *str, const char *delim);


头文件:#include <string.h>
参数:  

	str:      源字符串指针,即分割之前的字符串  
	delim:    用于分割的字符串指针,即分割符号。如:空格" "、 逗号","、 字符集合"@."等   
	
返回值:  
	成功:返回指向被分割出片段的指针  
	NULL:没有可被分割的字符串

即从字符串 str 中查找标记 delim 中出现的字符并以此切割字符串。注意原字符串 str 在函数调用完成后也会发生变化,变成分割后的第一个字符串。因此不能向第一个参数传递字符串常量,错误写法: char * str = "gonna be a string",应写成:char str[] = "gonna be a string"

二、字符串分割的步骤

在使用 strtok 函数时,第一次调用需要给定 str 参数的值,往后的每一次调用只需将 str 参数设置为 NULL 即可。我们通常使用 while 循环自动分割字符串的所有字符

  • 第一个参数不是 NULL 时,strtok 函数查找 str 下一个标记,以 \0 结尾,strtok 函数会保存标记的地址。
  • 第一个参数为 NULl 时,strtok 函数取上次分割字符串后剩余的字符串继续进行分解操作,如果没有更多的标记则返回 NULL

来举个例子:

#include <stdlib.h>
#include "stdio.h"
#include "string.h"
int main() {
        char *temp = NULL;
        char input[128] = "#10?-ssid=test&-passwd=12345678&-netmask=255.255.255.0&-gateway=192.168.1.1";
        //char *input = (char *)malloc(128); 
        //scanf("%s",input);
        printf("%s\n",input); 
        temp = strtok(input,"?&");
        printf("input=%s,temp=%s\n", input, temp);

        while(temp != NULL){
                temp = strtok(NULL,"?&");
                printf("input=%s,temp=%s\n", input, temp);
        };
        return 0;
}

输出结果:

#10?-ssid=test&-passwd=12345678&-netmask=255.255.255.0&-gateway=192.168.1.1
input=#10,temp=#10
input=#10,temp=-ssid=test
input=#10,temp=-passwd=12345678
input=#10,temp=-netmask=255.255.255.0
input=#10,temp=-gateway=192.168.1.1
input=#10,temp=(null)

可以看到,我们传入的字符串以参数 delim 为依据对字符串进行了切割,其实就是把字符串 str 在参数 delim 中出现的字符替换成了 \0,并且返回标记字符串的首地址,了解到这一点,那么就可以使用 strtok(NULL,' '); 对字符串进行分离输出了。我们把该字符串分割的情况用图像展示出来,可以看到分割的依据是参数 delim 中的 每一个字符
字符串分割示意图
所以在第一次分割后,str 的值会变成 #10 ,而后续的字符串需要我们用循环 strtok(NULL," "); 直至值为 NULL 来依照顺序把字符串提取出来。

char *
strtok (char *s, const char *delim)
{
        static char *olds;
        return __strtok_r (s, delim, &olds);
}

char *
__strtok_r (char *s, const char *delim, char **save_ptr)
{
        char *end;

        if (s == NULL)
                s = *save_ptr;
        //...
}

通过源码可以看到,其实 strtok 函数是把分割后剩余的字符串存储在了静态变量中,当函数的第一个参数传入 NULL 时再提取出来进行分解。因为 static 静态变量是全局变量,这种方式在多线程操作时或发生中断的情况下容易引起冲突,所以Linux中可以使用更加安全的 strtok_r 函数进行替换。

三、替代方案:strtok_r函数

strtok 函数是一个线程不安全的函数,多线程时,linux下可以用 strtok_r 函数代替,该函数与 strtok 相似,主要是多了第三个参数 saveptr

函数定义如下:

strtok_r()函数,将字符串分解为一组字符串,同时保证线程安全
声明:
char *strtok_r(char *str, const char *delim, char **saveptr);

头文件:#include <string.h>
参数:  

	str:      源字符串指针,即分割之前的字符串  
	delim:    用于分割的字符串指针,即分割符号。如:空格" "、 逗号","、 字符集合"@."等   
	saveptr:  保存分割后剩余的字符串
	
返回值:  
	成功:返回指向被分割出片段的指针  
	NULL:没有可被分割的字符串

该函数的第三个参数 saveptr 使用用户传入的指针重新申请变量来保存剩余的字符串,更好的保存了切分时的上下文,因此该函数就能被中断而不担心会丢失数据,并且变量保存在每个函数自己的栈,因此可以同时运行该函数的多个副本,在并行环境中保证了安全性。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐