strok、strtok_r和strtok_s

       strtok

       在处理文本的时候,我们最常用到的是读入字符串,然后对字符串进行处理。在分析、处理字符串的过程中就经常会用到根据特定分隔符对字符串进行分割。在C++语言中提供了strtok函数可以供我们完成字符串切割的功能。它的声明为

char	*strtok(char *__str, const char *__sep);  //__str是要分割的字符串,__sep是分隔符集合

          看一个例子:

char str[] = "hello Chris";
char *p = strtok(str, " ");
while(p != nullptr){
    cout << p << endl;
    p = strtok(nullptr, " ");
}
cout << str << Lendl;

输出:    Hello
         Chris
         Hello

        strtok将一个字符串“Hello Chris”,按照“ ”作为分隔符,分割成为“Hello”和“Chris”。

        在使用上第一次传入的是指向要被分割的字符串的指针,而后序需要传入nullptr。这里面只能够传入nullptr,因为可以看下面的输出,当第一次分割后,str的输出是Hello。这时候,str已经不再指向“Hello Chris”,而是指向“Hello”。这说明strtok对原字符串的分割是破坏性地分割。当strtok在参数str的字符串中发现参数delim中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok必需给予参数s字符串,往后的调用则将参数s设置成nullptr。每次调用成功则返回指向被分割出片段的指针。

        那么我们传入的是nullptr,strtok如何知道我们下一次要分割的字符串的起始位置那?这是因为strtok使用了静态分配的空间来存储被分割的字符串位置。这也就是说,当我们传入nullptr的时候,strtok就会去静态空间检查存储的指针,把这个指针取出来再进行分割。由于strtok这种实现形式,可以推测到1.strtok在多线程中使用时是不安全的;2.我们无法使用strtok将一个字符串进行多层次的分割。看下面的一个例子:

    char str[] = "boy,Lilei|girl,Lili";
    char *p = strtok(str, "|");
    while(p != nullptr){
        char *p1 = strtok(p, ",");
        while (p1 != nullptr){
            cout << p1 << endl;
            p1 = strtok(nullptr, ",");
        }
        p = strtok(nullptr, "|");
    }
   
    输出:   boy
            Lilei

        可以看到原意我们希望对字符串进行逐层解析,但是最后的结果并不是我们想要的。

        strtok_r

        为了解决上述strtok的弊端,C语言引进了strtok_r,我们先来看一下strtok_r的原型:

char	*strtok_r(char *__str, const char *__sep, char **__lasts);  //__lasts,保存下一次要分割字符串的指针

        在strtok_r中前两项的输入参数和strtok相同,而相比于strtok用一个静态变量保存下一次要分割字符串的指针,strtok_r使用一个单独的二维指针来保存。再看上面的例子,我们用strtok_r去实现。

    char str[] = "boy,Lilei|girl,Lili";
    char *savP, *savP1;
    char *p = strtok_r(str, "|", &savP);
    while(p != nullptr){
        char *p1 = strtok_r(p, ",", &savP1);
        while (p1 != nullptr){
            cout << p1 << endl;
            p1 = strtok_r(nullptr, ",", &savP1);
        }
        p = strtok_r(nullptr, "|", &savP);
    }

输出:   boy
        Lilei
        girl
        Lili

         可以看到实现我们最初的目的。

        strtok_s

       strtok_s和strtok_r有相同的实现形式和功能,不同的是strtok_s是windows环境下的实现形式,而strtok_r是Linux下的实现形式。

      对于strtok_s和strtok_r,我们可以对其进行封装成更加单的实现形式:

#if defined __GNUC__
#define STRTOK(savP, delm)     strtok_r(nullptr, delm, &savP)
#elif defined _MSC_VER
#define STRTOK(savP, delm)     strtok_s(nullptr, delm, &savP)
#endif    

        再来看上面的例子:

    char str[] = "boy,Lilei|girl,Lili";
    char *savP, *savP1;
    savP = str;
    char *p = STRTOK(savP, "|");
    while(p != nullptr){
        savP1 = p;
        char *p1 = STRTOK(savP1, ",");
        while (p1 != nullptr){
            cout << p1 << endl;
            p1 = STRTOK(savP1, ",");
        }
        p = STRTOK(savP, "|");
    }

输出:    boy
         Lilei
         girl
         Lili

        这样的形式更加简洁。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值