C中strtok()函数和strtok_r()函数

作者:buaa_shang 
原文:https://blog.csdn.net/buaa_shang/article/details/8189984 

1.

函数名:strtok

功能:查找由第二个串指定的分界符分开的单词

用法:char   *strtok(char   *str1,   char   *str2)

一个例子:

#include
#include
#include
 
int main()
{
        char input[16] = "abc,d";
        char *p; 
        p = strtok(input, ",");
        if(p) 
                printf("%s\n", p); 
        p = strtok(NULL, ",");
        if(p)
                printf("%s\n", p); 
        return 0;
}
第一次执行要以input为第一参数,第二次执行要以NULL为第一参数.
执行结果为

abc

d

第二个例子:

#include
#include
#include
 
int main()
{
        int in = 0;
        char buffer[20] = "Fred,John,Ann";
        char *p[3];
        char *buf = buffer;
        while((p[in] = strtok(buf, ",")) != NULL)
        {   
                in++;
                buf = NULL;
        }   
        printf("buffer = %s\n", buffer);
        printf("p1 = %s\n", p[0]);
        printf("p2 = %s\n", p[1]);
        printf("p3 = %s\n", p[2]);
}
执行结果为:
buffer = Fred
p1 = Fred
p2 = John
p3 = Ann
2.strtok()函数的弱点

如果一段字符串为"Fred male 25,John male 62,Anna female 16"

我们希望把这个字符串整理输出到一个struct

struct person

{

char name[25];

char sex[10];

char age[5];

}

我们首先想把上面的字符串按","分隔,然后再将第一次分隔后的字符串按" "(空个)分隔.

想法程序如下:

#include
#include
 
int main()
{
        int in = 0, j;
        char buffer[100] = "Fred male 25,John male 62,Anna female 16";
        char *p[20];
        char *buf = buffer;
        while((p[in] = strtok(buf, ",")) != NULL)
        {   
                buf = p[in];
                while((p[in] = strtok(buf, " ")) != NULL)
                {   
                        in++;
                        buf = NULL;
                }   
                p[in++] = "****";  //to present the symbol....
                buf = NULL;
        }   
        printf("Here we have %d strings \n", in);
        for(j = 0; j < in; j++)
                printf(">%s<\n", p[j]);
        return 0;
}
执行结果为:
>Fred<
>male<
>25<
>****<
显然没有得到我们想要的结果.

原因是:在第一次外循环中,strtok将"Fred male 25,"后的这个逗号,改为了'/0’,这时strtok内部的this指针指向的是逗号的后一个字符'J’。经过第一次的内循环,分别提取出了“Fred” “male” “25”。提取完"25”之后,函数内部的this指针被修改指向了"25”后面的'/0’。内循环结束后(内循环实际执行了4次),开始第二次的外循环,由于函数第一个参数被设定为NULL,strtok将以this指针指向的位置作为分解起始位置。很遗憾,此时this指针指向的是'/0’,strtok对一个空串无法切分,返回NULL。外循环结束。所以,我们只得到了如图所示的第一个人的信息。

2.上面问題的解决办法:使用strtok_r()函数.

函数原型:char *strtok_r(char *s, const char *delim, char **ptrptr);

strtok_r实际上就是将strtok内部隐式保存的this指针,以参数的形式与函数外部进行交互。由调用者进行传递、保存甚至是修改。需要调用者在连续切分相同源字符串时,除了将str参数赋值为NULL,还要传递上次切分时保存下的saveptr。

程序如下:

#include
#include
#include
 
int main()
{
        int in = 0, j;
        char buffer[100] = "Fred male 25,John male 62,Anna female 16";
        char *p[20];
        char *buf = buffer;
        char *outer_ptr = NULL;
        char *inner_ptr = NULL;
        while((p[in] = strtok_r(buf, ",", &outer_ptr)) != NULL) 
        {   
                buf = p[in];
                while((p[in] = strtok_r(buf, " ", &inner_ptr)) != NULL)
                {    
                        in++;
                        buf = NULL;
                }   
                buf = NULL;
        }   
        printf("Here we have %d strings\n", in);
        for(j = 0; j < in; j++)
                printf(">%s<\n", p[j]);
        return 0;
}
执行结果:
>Fred<
>male<
>25<
>John<
>male<
>62<
>Anna<
>female<
>16<

调用strtok_r的代码比调用strtok的代码多了两个指针,outer_ptr和inner_ptr。outer_ptr用于标记每个人的提取位置,即外循环;inner_ptr用于标记每个人内部每项信息的提取位置,即内循环。具体过程如下:

(1)第1次外循环,outer_ptr忽略,对整个源串提取,提取出"Fred male 25",分隔符',' 被修改为了'/0’,outer_ptr返回指向'J’。

(2)第一次内循环,inner_ptr忽略,对第1次外循环的提取结果"Fred male 25"进行提取,提取出了"Fred",分隔符' '被修改为了'/0',inner_ptr返回指向'm'。

(3)第二次内循环,传递第一次内循环返回的inner_ptr,第一个参数为NULL,从inner_ptr指向的位置'm'开始提取,提取出了"male",分隔符  ' '被修改为了'/0',inner_ptr返回指向'2'。

(4)第三次内循环,传递第二次内循环返回的inner_ptr,第一个参数为NULL,从inner_ptr指向的位置'2'开始提取,提取出了"25",因为没有找到' ',inner_ptr返回指向25后的'/0'。

(5)第四次内循环,传递第三次内循环返回的inner_ptr,第一个参数为NULL,因为inner_ptr指向的位置为'/0',无法提取,返回空值。结束内循环。

(6)第2次外循环,传递第1次外循环返回的outer_ptr,第一个参数为NULL,从outer_ptr指向的位置'J'开始提取,提取出"John male 62",分隔符',’被修改为了'/0’,outer_ptr返回指向'A’。(调用strtok则卡死在了这一步)

……以此类推,外循环一次提取一个人的全部信息,内循环从外循环的提取结果中,二次提取个人单项信息。

可以看到strtok_r将原内部指针显示化,提供了saveptr这个参数。增加了函数的灵活性和安全性。

--------------------- 

 

你可能感兴趣的:(C)