StringPiece谷歌实现

typedef BasicStringPiece<std::string> StringPiece;
template <typename STRING_TYPE> class BasicStringPiece {
 public:
  // Standard STL container boilerplate.
  typedef size_t size_type;
  typedef typename STRING_TYPE::value_type value_type;
  typedef const value_type* pointer;
  typedef const value_type& reference;
  typedef const value_type& const_reference;
  typedef ptrdiff_t difference_type;
  typedef const value_type* const_iterator;
  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

  static const size_type npos;
...
 protected:
  const value_type* ptr_;
  size_type     length_;
  };
  
  BasicStringPiece() : ptr_(NULL), length_(0) {}
  BasicStringPiece(const value_type* str)
      : ptr_(str),
        length_((str == NULL) ? 0 : STRING_TYPE::traits_type::length(str)) {}
  BasicStringPiece(const STRING_TYPE& str)
      : ptr_(str.data()), length_(str.size()) {}
  BasicStringPiece(const value_type* offset, size_type len)
      : ptr_(offset), length_(len) {}
  BasicStringPiece(const BasicStringPiece& str, size_type pos, size_type len = npos)
      : ptr_(str.data() + pos), length_(std::min(len, str.length() - pos)) {}
  BasicStringPiece(const typename STRING_TYPE::const_iterator& begin,
                    const typename STRING_TYPE::const_iterator& end)
      : ptr_((end > begin) ? &(*begin) : NULL),
        length_((end > begin) ? (size_type)(end - begin) : 0) {}

STRING_TYPE是模板参数类型,对StringPiece而言,STRING_TYPE也就是std::string
value_type是STRING_TYPE::value_type的别名,对StringPiece而言,也就是std::string::value_type ,即 char类型。

构造函数传入的如果是const char*,不指定长度的时候。StringPiece会调用string::traits_type::length()函数来计算长度,也就是std::char_traits::length

// char_traits::length
#include <iostream>   // std::cout
#include <string>     // std::char_traits

int main ()
{
  const char * foo = "String literal";
  std::cout << "foo has a length of ";
  std::cout << std::char_traits<char>::length(foo);
  std::cout << " characters.\n";
  return 0;
}
返回的是'\0'结尾的字符串的长度。如果你传入的const char*类型的字符串中间有'\0'会被截断。
https://cplusplus.com/reference/string/char_traits/length/

容器相关函数

  const value_type* data() const { return ptr_; }
  size_type size() const { return length_; }
  size_type length() const { return length_; }
  bool empty() const { return length_ == 0; }
...
  size_type max_size() const { return length_; }
  size_type capacity() const { return length_; }
  void clear() {
    ptr_ = NULL;
    length_ = 0;
  }

size()、length()、max_size()、capacity()这4个函数返回的都是length_,他们的值是相同的。这点和std::string是不同的。

数据修改函数

  BasicStringPiece& assign(const BasicStringPiece& str, size_type pos, size_type len = npos) {
    ptr_ = str.data() + pos;
    length_ = std::min(len, str.length() - pos);
    return *this;
  }
  void set(const value_type* data, size_type len) {
    ptr_ = data;
    length_ = len;
  }
  void set(const value_type* str) {
    ptr_ = str;
    length_ = str ? STRING_TYPE::traits_type::length(str) : 0;
  }
  void remove_prefix(size_type n) {
    ptr_ += n;
    length_ -= n;
  }

  void remove_suffix(size_type n) {
    length_ -= n;
  }
  void trim_spaces() {
    size_t nsp = 0;
    for (; nsp < size() && isspace(ptr_[nsp]); ++nsp) {}
    remove_prefix(nsp);
    nsp = 0;
    for (; nsp < size() && isspace(ptr_[size()-1-nsp]); ++nsp) {}
    remove_suffix(nsp);
  }       
  

修改其他字符串的函数

void CopyToString(STRING_TYPE* target) const {
    internal::CopyToString(*this, target);
  }

  // 将StringPiece指向的这段字符串追加到target指向的位置
  void AppendToString(STRING_TYPE* target) const {
    internal::AppendToString(*this, target);
  }

  // 将StringPiece指向的这段字符串从pos位置开始复制n个字符到buf开始的位置中
  size_type copy(value_type* buf, size_type n, size_type pos = 0) const {
    return internal::copy(*this, buf, n, pos);
  }

CopyToString()本质是调用的std::string的assign函数。AppendToString()本质是调用的std::string的append()函数。copy本质调用的是memcpy()。

数据访问函数

  value_type operator[](size_type i) const { return ptr_[i]; }
  char front() const { return *ptr_; }
  char back() const { return *(ptr_ + length_ - 1); }
  // Return the first/last character, 0 when StringPiece is empty.
  char front_or_0() const { return length_ ? *ptr_ : '\0'; }
  char back_or_0() const { return length_ ? *(ptr_ + length_ - 1) : '\0'; }

std::string支持const和非const两个重载,而StringPiece只有const版本。也就是无法通过[]来修改原类型的。其二就是std::string返回的都是引用(const版本是const引用),而StringPiece返回的值类型。这也不难理解,主要目的就是让Stringpiece的[]返回的单个字符和原字符串的生命周期解耦。(StringPiece内心OS:整个字符串的生命周期都是你的了,单个字符还是我说了算吧,不然也太不安全了)
支持front()、back()直接返回首尾元素,同样是const函数,且返回的是值。和普通std::string不同。比std::string多两个函数:front_or_0()和back_or_0(),他们是当字符串长度为0的时候,返回一个’\0’字符。

  const_iterator begin() const { return ptr_; }
  const_iterator end() const { return ptr_ + length_; }
  const_reverse_iterator rbegin() const {
    return const_reverse_iterator(ptr_ + length_);
  }
  const_reverse_iterator rend() const {
    return const_reverse_iterator(ptr_);
  }

比较函数

  // Does "this" start with "x"
  bool starts_with(const BasicStringPiece& x) const {
    return ((this->length_ >= x.length_) &&
            (wordmemcmp(this->ptr_, x.ptr_, x.length_) == 0));
  }

  // Does "this" end with "x"
  bool ends_with(const BasicStringPiece& x) const {
    return ((this->length_ >= x.length_) &&
            (wordmemcmp(this->ptr_ + (this->length_-x.length_),
                        x.ptr_, x.length_) == 0));
  }
    static int wordmemcmp(const value_type* p,
                        const value_type* p2,
                        size_type N) {
    return STRING_TYPE::traits_type::compare(p, p2, N);
  }
  static int compare (const char_type* p, const char_type* q, size_t n) {
  while (n--) {if (!eq(*p,*q)) return lt(*p,*q)?-1:1; ++p; ++q;}
  return 0;
}https://cplusplus.com/reference/string/char_traits/compare/

bool operator==(const StringPiece& x, const StringPiece& y) {
  if (x.size() != y.size())
    return false;

  return StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0;
}
inline bool operator<(const StringPiece& x, const StringPiece& y) {
  const int r = StringPiece::wordmemcmp(
      x.data(), y.data(), (x.size() < y.size() ? x.size() : y.size()));
  return ((r < 0) || ((r == 0) && (x.size() < y.size())));
}

查找函数

  // find: Search for a character or substring at a given offset.
  size_type find(const BasicStringPiece<STRING_TYPE>& s,
                 size_type pos = 0) const {
    return internal::find(*this, s, pos);
  }
  size_type find(value_type c, size_type pos = 0) const {
    return internal::find(*this, c, pos);
  }

  // rfind: Reverse find.
  size_type rfind(const BasicStringPiece& s,
                  size_type pos = BasicStringPiece::npos) const {
    return internal::rfind(*this, s, pos);
  }
  size_type rfind(value_type c, size_type pos = BasicStringPiece::npos) const {
    return internal::rfind(*this, c, pos);
  }
  
  size_t find(const StringPiece& self, const StringPiece& s, size_t pos) {
   return findT(self, s, pos);
  }
  size_t find(const StringPiece& self, char c, size_t pos) {
  	return findT(self, c, pos);
  }

template<typename STR>
size_t findT(const BasicStringPiece<STR>& self,
             const BasicStringPiece<STR>& s,
             size_t pos) {
  if (pos > self.size())
    return BasicStringPiece<STR>::npos;

  typename BasicStringPiece<STR>::const_iterator result =
      std::search(self.begin() + pos, self.end(), s.begin(), s.end());
  const size_t xpos =
    static_cast<size_t>(result - self.begin());
  return xpos + s.size() <= self.size() ? xpos : BasicStringPiece<STR>::npos;
}

template<typename STR>
size_t findT(const BasicStringPiece<STR>& self,
             typename STR::value_type c,
             size_t pos) {
  if (pos >= self.size())
    return BasicStringPiece<STR>::npos;

  typename BasicStringPiece<STR>::const_iterator result =
      std::find(self.begin() + pos, self.end(), c);
  return result != self.end() ?
      static_cast<size_t>(result - self.begin()) : BasicStringPiece<STR>::npos;
}
可以看出对于查找字符串其本质是使用的STL的search函数,而查找单个字符的时候使用的是STL的find()函数。不过对于rfind()则并不相同。它的实现如下:
template<typename STR>
size_t rfindT(const BasicStringPiece<STR>& self,
              const BasicStringPiece<STR>& s,
              size_t pos) {
  if (self.size() < s.size())
    return BasicStringPiece<STR>::npos;

  if (s.empty())
    return std::min(self.size(), pos);

  typename BasicStringPiece<STR>::const_iterator last =
      self.begin() + std::min(self.size() - s.size(), pos) + s.size();
  typename BasicStringPiece<STR>::const_iterator result =
      std::find_end(self.begin(), last, s.begin(), s.end());
  return result != last ?
      static_cast<size_t>(result - self.begin()) : BasicStringPiece<STR>::npos;
}

template<typename STR>
size_t rfindT(const BasicStringPiece<STR>& self,
              typename STR::value_type c,
              size_t pos) {
  if (self.size() == 0)
    return BasicStringPiece<STR>::npos;

  for (size_t i = std::min(pos, self.size() - 1); ;
       --i) {
    if (self.data()[i] == c)
      return i;
    if (i == 0)
      break;
  }
  return BasicStringPiece<STR>::npos;
}
对于字符串的反向查找本质的调用的STL的find_end。而对于字符的反向查找是完全手写实现的。
另外StringPiece也支持std::string中的find_first_of()、find_first_not_of()、find_last_of()、find_last_not_of()。这里只展开一个:
size_t find_first_of(const StringPiece& self,
                     const StringPiece& s,
                     size_t pos) {
  if (self.size() == 0 || s.size() == 0)
    return StringPiece::npos;

  // Avoid the cost of BuildLookupTable() for a single-character search.
  if (s.size() == 1)
    return find(self, s.data()[0], pos);

  bool lookup[UCHAR_MAX + 1] = { false };
  BuildLookupTable(s, lookup);
  for (size_t i = pos; i < self.size(); ++i) {
    if (lookup[static_cast<unsigned char>(self.data()[i])]) {
      return i;
    }
  }
  return StringPiece::npos;
}
inline void BuildLookupTable(const StringPiece& characters_wanted,
                             bool* table) {
  const size_t length = characters_wanted.length();
  const char* const data = characters_wanted.data();
  for (size_t i = 0; i < length; ++i) {
    table[static_cast<unsigned char>(data[i])] = true;
  }
}
映射表用数组实现,数组下标就是对应字符的数值,value是是否在StringPiece中出现过。后面就是普通的遍历find了。其他的其他函数实现也是类似。

截取子串

StringPiece substr(const StringPiece& self,
                   size_t pos,
                   size_t n) {
  return substrT(self, pos, n);
}

template<typename STR>
BasicStringPiece<STR> substrT(const BasicStringPiece<STR>& self,
                              size_t pos,
                              size_t n) {
  if (pos > self.size()) pos = self.size();
  if (n > self.size() - pos) n = self.size() - pos;
  return BasicStringPiece<STR>(self.data() + pos, n);
}

返回string对象

 STRING_TYPE as_string() const {
    // std::string doesn't like to take a NULL pointer even with a 0 size.
    return empty() ? STRING_TYPE() : STRING_TYPE(data(), size());
  }

从StringPiece到string_view

在各个C++开源项目提供了不同版本StringPiece的许多年以后,事情开始有了变化。C++14开始,string_view作为实验功能被进入到C++标准。<experimental/string_view>头文件中有std::experimental::string_view 这一新增的字符串视图类型。C++17 开始string_view顺利转正。头文件<string_view>正式纳入标注,std::string_view类型进入大家的视野。

API的差异

比如string_view没有assign()、clear()以及as_string()函数。访问函数增加了at(),和一般的string类似。

string_view有front()和back(),但没有front_or_0()、back_or_0()。并且front()和back()返回的是 constexpr const_reference。当string_view为空的时候,此时调用front()和back()是未定义行为(UB)!这点要注意。

C++20开始string_view加入了starts_with()和ends_with()。C++23中多了一个contains()可以用来查找一个const char*、string_view、char是否在当前string_view中,返回一个bool类型。而StringPiece中没有contains()。

最主要的是string_view在C++的语法层面,增加了一个运算符sv

#include <string_view>
#include <iostream>
using namespace std::literals;
int main() {
    auto print = [](std::string_view sv) {
        std::cout << "size:" << sv.size() << std::endl;
        for (char c: sv) {
            std::cout << c << std::endl;
        }
        std::cout<<"----"<<std::endl;
    };

    std::string_view s1 = "abc\0\0def";
    std::string_view s2 = "abc\0\0def"sv;
    print(s1);
    print(s2);
    print(s1.substr(1, 5));
    print(s2.substr(1, 5));
}
输出
size:3
a
b
c
----
size:8
a
b
c


d
e
f
----
size:2
b
c
----
size:5
b
c


d
----

在""双引号表达的C风格字符作为参数,且不指定长度来构造string_view对象的时候,其表现和StringPiece一致,会默认以’\0’结尾的字符作为string_view的有效字符串。而加了sv后缀以后则能将整个字符串视为有效字符串。

没有享受到C++14或17编译器的小伙伴们,可以使用boost库中的boost::string_view

https://zhuanlan.zhihu.com/p/98829229

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值