前一段时间在工作过程中遇到一个场景需要将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的最大值,也就是说在这个函数中为了避免字符串过长导致函数内部使用的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;
}

最新文章

  1. c# Enumerable中Aggregate和Join的使用
  2. Progressive JPEG
  3. sql 指令
  4. Raspberry PI Model B+ (LCD显示CPU温度)
  5. mysql--学生课程成绩表
  6. LNMP之编译安装PHP出现的问题
  7. ios初体验&lt; 运用属性传值,登录&gt;
  8. 2018年1月 mybatis+ 动态sql连续日期
  9. RxJava(五) onErrorResumeNext操作符实现app与服务器间token机制
  10. jetty8 text/plain默认字符编码的坑
  11. linu系统文件授权命令
  12. [译] 关于 Angular 依赖注入你需要知道的
  13. CRM项目之stark组件(2)
  14. 【知识碎片】getResource和getResourceAsStream
  15. NOI.ac #31 MST DP、哈希
  16. JS中获取文件点之后的后缀字符
  17. UploadFtp
  18. JAVA I/O(四)网络Socket和ServerSocket
  19. android机顶盒真机调试方法
  20. shell 前台进程组的选择

热门文章

  1. MySQL之LIMIT用法
  2. spring中scope的prototype与singleton区别
  3. HTML01--基础概述
  4. String相关练习
  5. pytest文档博客链接
  6. 谈谈数据库的ACID
  7. JavaScript定时器与执行机制
  8. ACM练习网站
  9. Android开源项目xUtils HttpUtils模块分析(转)
  10. docker最新版本如何自定义配置文件