分隔字符串

c函数strtok_s(Linux上是strtok_r)可以分隔字符串,原型如下:

char *strtok_s(
   char *strToken,
   const char *strDelimit,
   char **context
);

 

示例如下:

    //
    // 功能:解析字符串szSrc,分隔符为szDelimit中的任一字符
    //
    char szSrc[] = "-abc-=-def";
    char szDelimit[] = "-=";

    // 由于strtok_s会破坏原始字符串,所以拷贝一份出来解析
    char *pszDuplicate = strdup(szSrc);
    char *pszToken = NULL;        // 分隔后的子字符串
    char *pszRemainder = NULL;    // 每次解析后的剩余字符串

    // 第一次解析原始字符串
    pszToken = strtok_s(pszDuplicate, szDelimit, &pszRemainder);
    while (NULL != pszToken && NULL != pszRemainder)
    {
        // 之后就解析上次解析后的剩余字符串
        pszToken = strtok_s(NULL, szDelimit, &pszRemainder);
    }

    if (NULL != pszDuplicate)
    {
        free(pszDuplicate);
        pszDuplicate = NULL;
    }
 

由于strtok_s会修改原始字符串的内容,所以我们可以基于strtok_s的源代码,写一个不修改原始字符串的替代函数strtok_s2,函数的实现详细介绍了算法原理:

// 与标准c函数strtok_s不同,strtok_s2不修改*ppszRemainder的内容。
// 返回值只去掉了前面的分隔符,但返回的字符串中间或后面可能仍然包含分隔符,所以
// 需要通过*ppszRemainder减去返回值来确定长度。
const char *strtok_s2(__inout const char **ppszRemainder, __in const char *pszDelimit)
{
    if (NULL == ppszRemainder || NULL == *ppszRemainder || NULL == pszDelimit)
    {
        return NULL;
    }

    // 定义一个bitmap,每一位代表一个控制字符,所以可以保存4*32=128个控制字符
    // 因为可见字符不会超过128个,所以这个bitmap足够容纳所有可见字符。
    unsigned char map[32] = { 1 };
    for (int count = 0; count < 32; count++)
    {
        map[count] = 0;
    }

    // 把分隔符表中的字符放到bitmap中。算法如下:
    // 分隔符高5位作为bitmap中的索引,因为二进制的11111等于十进制的31,所以0-31正好是32个数。
    // 而分隔符的低3位(二进制最大值111,十进制 7)表示把1左移的位数,由于最多左移7位,
    // 所以,最大可以表示1000 0000, 最小表示0000 0001,正好占满一个字节。左移后的值,或到
    // 上面bitmap的“通过分隔符高5位”计算出来的索引中。所以,一个bitmap元素可以保存8个分隔符。
    // 而32个元素可以保存128个分隔符。
    // 比如分隔符为';',二进制值为00111 011:
    //        高5位的00111(十进制7),表示在map数组中的索引,即分隔符保存到map[7]中;
    //        低3位的011(十进制3),表示把1左移3位的值1000或到map[7]上。
    // 由于每个分隔符分解后是或到元素值上的,所以这个算法还可以实现分隔符去重。
    unsigned char const *unsigned_control = reinterpret_cast<unsigned char const *>(pszDelimit);
    do
    {
        // *unsigned_control >> 3:得到分隔符高5位。
        // (*unsigned_control & 7):得到分隔符低3位。
        map[*unsigned_control >> 3] |= (1 << (*unsigned_control & 7));
    }
    while (*unsigned_control++);

    // 如果string为NULL,解析*context,否则解析string
    const char *it = *ppszRemainder;

    const unsigned char *&unsigned_it = reinterpret_cast<const unsigned char *&>(it);

    // 找到第一个不以分隔符开始的字符串
    while ((map[*unsigned_it >> 3] & (1 << (*unsigned_it & 7))) && *it)
    {
        ++it;
    }

    // token_first:第一个不以分隔符开始的字符串
    const char *const token_first = it;

    // 查找下一个分隔符,查到后把相应位置的字符置0。
    for (; *it; ++it)
    {
        if (map[*unsigned_it >> 3] & (1 << (*unsigned_it & 7)))
        {
            //*it++ = '\0';
            break;
        }
    }

    // 更新剩余字符串指针
    *ppszRemainder = it;

    // 返回找到的分隔后的字符串
    return it != token_first ? token_first : nullptr;
}
 

示例代码:

    // 需要被解析的字符串
    const char szSrc[] = "-abc-=-def";
    // 分隔符集,里面的任何一个字符都是分隔符
    const char szDelimit[] = "-=";
    // 分隔后的子字符串
    const char *pszToken = NULL;
    // 每次解析后的剩余字符串
    const char *pszRemainder = szSrc;
    // 每次解析后分隔出来的字符串
    std::string strToken;

    while (NULL != (pszToken = strtok_s2(&pszRemainder, szDelimit)))
    {
        strToken.assign(pszToken, pszRemainder - pszToken);
    }
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值