Chromium base库分割字符串SplitString
前一段时间在工作过程中遇到一个场景需要将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类型的字符串,下图是大体流程图
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的最大值,也就是说在这个函数中为了避免字符串过长导致函数内部使用的start
和end
发生溢出。
最基本就是循环遍历原始字符串,主要使用到了findfirstof
函数,函数findfirstof
的实现如下(只看分界符是单个字符char
的情况)
size_t findfirstof(stringpiece piece, char c, size_t pos) { return piece.find(c, pos); }
这里的find
函数和std::string
的find
函数功能一致,从当前pos
位置开始想后查询c
字符,找到第一个并返回其所在位置。如果找到了就更新end
的值,然后取start
和end
之间的子字符串,更新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; }
下一篇: 你是什么眼力