欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Chromium base库分割字符串SplitString

程序员文章站 2022-07-26 13:18:46
前一段时间在工作过程中遇到一个场景需要将http response中的request header中的cookie字段取出并进行解析,但是手头没有解析cookie的工具类,同时cookie的表现就是个字符串,于是想到手动分割 但是在C++的标准库中,并没有提供类似split的函数,在有些时候可能会很 ......

前一段时间在工作过程中遇到一个场景需要将http response中的request header中的cookie字段取出并进行解析,但是手头没有解析cookie的工具类,同时cookie的表现就是个字符串,于是想到手动分割
但是在c++的标准库中,并没有提供类似split的函数,在有些时候可能会很不方便,今天就看看google大佬是如何实现字符串的分割的。
在chromium的base库中有提供和字符串相关的函数,在base/strings/string_split.h和base/strings/string_split.cc中定义了splitstring函数用于分割std::string类型的字符串,下图是大体流程图
Chromium base库分割字符串SplitString

std::vector<std::string> splitstring(stringpiece input,
                                     stringpiece separators,
                                     whitespacehandling whitespace,
                                     splitresult result_type) {
  if (separators.size() == 1) {
    return splitstringt<std::string, std::string, char>(
        input, separators[0], whitespace, result_type);
  }
  return splitstringt<std::string, std::string, stringpiece>(
      input, separators, whitespace, result_type);
}

其中stringpiece是google定义的一种字符串类型,是对std::string的一种封装,这里就不再多说,可以直接看成std::string
函数这里传入的四个参数分别是输入字符串,分割符,遇到空格处理(保留,跳过),结果类型(保留空值,不保留)
可以看到google根据传入的分割符的长度做了两种处理方式,说是做了两种处理方式,但其实就是讲分割符一个看成单个字符char,一个看成std::string字符串而已,这与splitstringt模板的具体实现有关。下面重头戏来了,看下splitstringt是如何实现的

template<typename str, typename outputstringtype, typename delimitertype>
static std::vector<outputstringtype> splitstringt(
    basicstringpiece<str> str,
    delimitertype delimiter,
    whitespacehandling whitespace,
    splitresult result_type) {
  std::vector<outputstringtype> result;
  if (str.empty())
    return result;

  size_t start = 0;
  while (start != str::npos) {
    size_t end = findfirstof(str, delimiter, start);

    basicstringpiece<str> piece;
    if (end == str::npos) {
      piece = str.substr(start);
      start = str::npos;
    } else {
      piece = str.substr(start, end - start);
      start = end + 1;
    }

    if (whitespace == trim_whitespace)
      piece = trimstring(piece, whitespacefortype<str>(), trim_all);

    if (result_type == split_want_all || !piece.empty())
      result.push_back(piecetooutputtype<str, outputstringtype>(piece));
  }
  return result;
}

模板参数的三个定义分别是<传入被分割字符串的类型,输出vector的模板类型,分界符类型>,函数的四个参数和上面说的相同,其中第三个第四个参数主要是对空值和空格的取舍。
str::npos指的是size_t的最大值,也就是说在这个函数中为了避免字符串过长导致函数内部使用的startend发生溢出。
最基本就是循环遍历原始字符串,主要使用到了findfirstof函数,函数findfirstof的实现如下(只看分界符是单个字符char的情况)

size_t findfirstof(stringpiece piece, char c, size_t pos) {
  return piece.find(c, pos);
}

这里的find函数和std::stringfind函数功能一致,从当前pos位置开始想后查询c字符,找到第一个并返回其所在位置。如果找到了就更新end的值,然后取startend之间的子字符串,更新start的值。如果找到下一个分界符了,这个函数返回result就结束了。

剩下最后的部分就是对空格和空值的取舍,下面是取舍部分的代码,取自splitstringt函数

if (whitespace == trim_whitespace)
      piece = trimstring(piece, whitespacefortype<str>(), trim_all);

    if (result_type == split_want_all || !piece.empty())
      result.push_back(piecetooutputtype<str, outputstringtype>(piece));

空值取舍就是一个判断,这里不再描述,就只看空格取舍,whitespacefortype<str>()主要是提供一个模板空格,根据传入的str类型不同空格也有可能不同,而trim_all的主要作用如下

enum trimpositions {
  trim_none     = 0,
  trim_leading  = 1 << 0,
  trim_trailing = 1 << 1,
  trim_all      = trim_leading | trim_trailing,
};

用于区分空格类型,头部空格和尾部空格。默认是trim_all全部。下面是trimstring函数的实现

stringpiece trimstring(stringpiece input,
                       stringpiece trim_chars,
                       trimpositions positions) {
  return trimstringpiecet(input, trim_chars, positions);
}
template<typename str>
basicstringpiece<str> trimstringpiecet(basicstringpiece<str> input,
                                       basicstringpiece<str> trim_chars,
                                       trimpositions positions) {
  size_t begin = (positions & trim_leading) ?
      input.find_first_not_of(trim_chars) : 0;
  size_t end = (positions & trim_trailing) ?
      input.find_last_not_of(trim_chars) + 1 : input.size();
  return input.substr(begin, end - begin);
}

用的函数也和std::string的成员函数功能一致,是很简单的去除空格的方式。

附录

我略微整理了一下一个vs可直接编译运行的版本(几乎没啥改动就是了)

#include <iostream>
#include <vector>
#include <string>

enum trimpositions {
    trim_none = 0,
    trim_leading = 1 << 0,
    trim_trailing = 1 << 1,
    trim_all = trim_leading | trim_trailing,
};

size_t findfirstof(std::string piece, std::string c, size_t pos) {
    return piece.find(c, pos);
}

std::string trimstring(std::string input,
    trimpositions positions) {
    size_t begin = (positions & trim_leading) ?
        input.find_first_not_of(" ") : 0;
    size_t end = (positions & trim_trailing) ?
        input.find_last_not_of(" ") + 1 : input.size();
    return input.substr(begin, end - begin);
}

std::vector<std::string> splitstring(std::string str,
    std::string c,
    bool skip_whitespace,
    bool skip_empty) 
{
    std::vector<std::string> result;
    if (str.empty())
        return result;

    size_t start = 0;
    while (start != std::string::npos) {
        size_t end = findfirstof(str, c, start);

        std::string piece;
        if (end == std::string::npos) {
            piece = str.substr(start);
            start = std::string::npos;
        }
        else {
            piece = str.substr(start, end - start);
            start = end + 1;
        }

        if (skip_whitespace)
            piece = trimstring(piece, trim_none);

        if (!skip_empty || !piece.empty())
            result.push_back(piece);
    }
    return result;
}


int main()
{
    std::vector<std::string> result = splitstring("url=https://www.baidu.com", ".", true, false);
    return 0;
}