前一段时间在工作过程中遇到一个场景需要将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;
}

Chromium base库分割字符串SplitString的更多相关文章

  1. VC/MFC分割字符串(SplitString)返回CStringArray

    引自:http://bbs.csdn.net/topics/60321228 原版: CStringArray* SplitString(CString string, char pattern) { ...

  2. 利用MySQL存储过程分割字符串

    (转)http://tec.5lulu.com/detail/104krn1e6p2w78d77.html 现有一段字符串,如apple,banana,orange,pears,grape,要把它按照 ...

  3. (三)Boost库之字符串处理

    (三)Boost库之字符串处理 字符串处理一直是c/c++的弱项,string_algo库很好的弥补了这一点. string_algo 库算法命名规则: 前缀i    : 有这个前缀表名算法的大小写不 ...

  4. C语言的本质(22)——C标准库之字符串操作

    编译器.浏览器.Office套件等程序的主要功能都是符号处理,符号处理功能在程序中占相当大的比例,无论多复杂的符号处理都是由各种基本的字符串操作组成的,下面介绍如何用C语言的库函数做字符串初始化.取长 ...

  5. c/c++分割字符串

    c++分割字符串: http://www.martinbroadhurst.com/how-to-split-a-string-in-c.html c分割字符串: http://www.martinb ...

  6. UDF_表值函数与标量函数的区别_分割字符串成单个的字符并返回表(插入到表中)

    UDF_区别_分割字符串成单个的字符并返回表(插入到表中) /* SQL表值函数和标量值函数的区别 实验环境:SQL Server 2014,参考maomao365有改编 在sqlserver中存储过 ...

  7. Delphi中stringlist分割字符串的用法

    Delphi中stringlist分割字符串的用法 TStrings是一个抽象类,在实际开发中,是除了基本类型外,应用得最多的. 常规的用法大家都知道,现在来讨论它的一些高级的用法. 1.CommaT ...

  8. C语言分割字符串

    最近在做一道C语言题目的时候需要用到分割字符串,本来想自己手写的,也不会很麻烦,但想到其他语言都有分割字符串的库函数,C语言怎么会没有呢?所以,在网上搜了一搜,果然有这样的函数,还是很好用的,在此总结 ...

  9. Android--split()分割字符串特殊用法

    split()分割字符串 1.不同环境下的区分 Java:分割字符串不能写成split("$")//$为要分割的字符Android:分割字符串需要加上中括号split(" ...

随机推荐

  1. oracle常用cmd命令

    登陆 sqlplus username/password; 切换: conn username/password; 显示当前登陆用户: show user; 查看用户列表 select usernam ...

  2. Hyper-V如何新建虚拟机

    http://www.xitongtiandi.net/wenzhang/soft/24543.html

  3. Q138 复制带随机指针的链表

    给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点. 要求返回这个链表的深拷贝. 示例: 输入: {"$id":"1",& ...

  4. Microsoft Power BI Desktop概念学习系列之Microsoft Power BI Desktop的官网自带示例数据(图文详解)

    不多说,直接上干货! https://docs.microsoft.com/zh-cn/power-bi/sample-datasets 后续的博文系列,将进行深入的剖析和分享. 欢迎大家,加入我的微 ...

  5. Types方法之isSameType-isSuperType-isSubType

    4.isSameType() 方法 /** * Is t the same type as s? */ public boolean isSameType(Type t, Type s) { retu ...

  6. unity编辑器教程

    https://blog.csdn.net/candycat1992/article/details/52067975

  7. Java Servelet

    1.服务器端运行的程序 2.Servelet三个方法 init service 抽象方法 destory 这三个方法构成了servelet的生命周期 3.步骤 1.在web.xml中 描述了servl ...

  8. Session 与 Cookie

    Session 与 Cookie 的作用都是为了保持访问用户与后端服务器的交互状态.它们本身只是HTTP中的一个配置项,在servlet规范中也只对应一个类而已. 理解 Cookie 通俗地说就是当一 ...

  9. WPF中设置了WindowStyle="None"后,窗口仍然有边框的解决方法

    1. 设置了窗体的WindowStyle="None",窗口还是右边框,如下图: 2. 这是因为窗体默认是可以改变大小的,所以需要修改ResizeMode的值 ResizeMode ...

  10. Java的接口(interface)属性和方法的类型

    接口的属性必须是public static final Type 接口的方法必须是public abstract Type 不管你是全写,或只写部分,系统都会自动按上面的要求不全 也就是说 接口中 所 ...