致力于在现代C++中提供Python的编程体验。这个建立在两个关键的基础上

但是 python 体验是建立在语言内建的大量方便函数的基础之上的。而 Modern C++ 还缺少大量使用起来方便的库。

我们今天要来解决的第一个问题是如何在C++中表达字符串。选择一个字符串的实现主要有三方面的考虑:

  • 是值对象还是引用对象:std::string 是值对象,std::string_view 是引用对象。值对象即便用上了所有的优化(stack分配内存,ref counted cow)仍然是有开销的。在该使用应用的地方,仍然应该使用引用。

  • 值对象的内存管理。folly::fbstring 就比 std::string 更好地利用了内存

  • 支持 unicode:不应该把字符串简单看成一个char数组

市面上几乎所有的string抽象对unicode支持都不好。我希望的是类似u'中文字符'这样的使用体验。c++ 11/14/17 在unicode支持方面支持得非常有限:

  • 虽然引入了char16_t 和 char32_t,但是你以为字符串就是 vector<char32_t> 就太天真了。使用的时候需要各种字符转换到char32_t不说,而且locale居然不支持char32_t,意味着基本的toupper都无法实现

  • regex 不支持 unicode,. 是无法匹配一个完整的code point 的

https://github.com/CaptainCro... 这个封装是最接近的,但是不是基于 range-v3 的。

目前的我实现的代码在:https://github.com/taowen/pyt...

定义一个字符串常量

auto str = U8("中文字符");

U8是一个宏,可以保证u8""这样字符串声明(c++ 编译器支持的 utf8 编码),同时额外做的工作是让构造出来的字符串和普通的字符串类型不兼容,避免误用。也就是 string != unicode 。这个就相当于

str = u'中文字符'

取得字符串长度

CHECK(2 == (U8("中文") | pyn::len));

这个就相当于

len(u'中文')

如果不喜欢 | 的调用语法,也可以用普通的函数

pyn::len(U8("中文"));

首字母大写

CHECK(U8("Hello") == (U8("hello") | utf8::capitalize | utf8::to_text));

注意这里 capitalize 并不会立即返回一个新的字符串(分配一个新的内存空间,然后拷贝字符),而是返回了一个类似python generator的东西。在 to_text 的时候才会分配内存,进行计算。默认 to_text 返回的字符串类型是 utf8::Text 底层存储是 folly::fbstring

这里定义的字符串并不能直接遍历或者下标访问,因为在unicode中什么一个“character“是有歧义的,把字符串看成一个数组是一个过时的想法(即便是16位的char)。字符串就没有常量时间可随机访问这么一说。如果需要遍历,需要这么写

auto chars = std::vector<char>{};
for (auto c : U8("abc") | utf8::code_units) {
    chars.push_back(c);
}
CHECK((std::vector<char>{'a', 'b', 'c'}) == chars);

这个取得的是code unit,在非ascii范围内的字符会读取不完整。

auto chars = std::vector<utf8::TextView>{};
for (auto c : U8("中文") | utf8::code_points) {
    chars.push_back(c);
}
CHECK((std::vector<utf8::TextView>{U8("中"), U8("文")}) == chars);

这里的遍历就类似

chars = []
for c in u'中文':
  chars.append(c)
[u'中', u'文'] == c

我们这里干的事情类似于python 2.7 里 string/unicode 两种类型的区分。我们需要一种存储上是string(utf8编码),但是使用起来类似 unicode 的抽象。目前来看,前途光明。


taowen
4.1k 声望1.4k 粉丝

Go开发者们请加入我们,滴滴出行平台技术部 taowen@didichuxing.com