• 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的最大值,也就是说在这个函数中为了避免字符串过长导致函数内部使用的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;
    }
    
  • 相关阅读:
    Collatz Conjecture(BAPC2017)
    P3377 【模板】左偏树(可并堆)
    Mergeable Stack
    B. Our Tanya is Crying Out Loud(cf)and 5918: 改变(中石油)
    bzoj 4488: [Jsoi2015]最大公约数
    Zoj
    牛客练习赛43 回顾
    哈尔滨工程大学ACM预热赛 补题
    April Fools Day Contest 2019: editorial回顾补题
    第一周总结
  • 原文地址:https://www.cnblogs.com/lenomirei/p/10243679.html
Copyright © 2020-2023  润新知