Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

字符串

复习

  • 文字与编码:文字靠字符编码表示,ASCII、Unicode 与 UTF-8 解决编号与存储问题
  • 数组:连续存储,可随机访问
  • 抽象数据类型:先规定能做什么,再决定怎么做

TL;DR

  • 字符串是字符的序列,底层常基于数组
  • 编码决定了“一个字符占多少字节”
  • 遍历、查找子串、切分、拼接是常见操作
  • 有些语言里字符串不可变,安全便利,但有代价

正文

  我们已经用过很多次字符串了。可它说到底,就是一种字符的序列——如果回想编码那一部分,字符又都是由二进制编号表示的。

底层其实就是数组

  在大多数实现里,字符串的底层就是一个字符数组:字符一个挨一个地连续存放。

  所以字符串天然继承了数组的特点:

  • 可以按下标访问某个位置,O(1)
  • 在中间插入或删除字符,需要挪动后面的部分,O(n)

  但这里有一个特别容易踩的坑:字符串的“第几个字符”和“第几个字节”,在有变长编码时并不一致。

  比如 UTF-8 里,一个英文字母只占 1 个字节,可一个汉字往往占 3 个字节。于是“取第 5 个字符”,并不能简单地取第 5 个字节——必须按编码规则一路数过去。这正是编码知识会影响数据结构操作的一个典型例子。

常见操作

  字符串上有一些高频操作:

  • 遍历:逐个处理字符
  • 查找子串:在一串里找另一串
  • 切分:按分隔符拆成若干段
  • 拼接:把两串接在一起

  要留意拼接的代价。如果字符串底层是不可变的,那么每次拼接都可能需要新建一整串并复制内容,反复拼接会变得很慢。这也是为什么有些语言会提供专门的“可变字符串”类型,避免反复复制。

不可变:好处与代价

  上文提到的“不可变”(immutable),是很多语言里字符串的重要特性:一旦创建,内容就不能被修改,只能生成新串。

  它带来不少好处:

  • 可以安全地在多处共享,不必担心被别人改掉
  • 适合作为查找的键,因为内容不会变
  • 并发使用时更省心

  代价则是:任何“修改”都要生成新串,反复拼接或修改时开销较大。

  这又是一次熟悉的权衡:用“不能改”换来安全与共享,也要为“每次改动都新建”付出代价。

思考题 1

  在 UTF-8 字符串里,“第 5 个字符”为什么不能简单地取第 5 个字节?

思考题 2

  字符串“不可变”有什么好处,又有什么代价?

小结

知识点

  • 字符串是字符序列,底层常基于数组
  • 变长编码下,字符位置与字节位置不一致
  • 遍历、查找、切分、拼接是常见操作
  • 不可变带来安全与共享,代价是修改要新建

参考资料

  1. Wikipedia(zh):字符串:由字符组成的序列
  2. Wikipedia(zh):UTF-8:一种变长的 Unicode 编码

思考题答案(仅供参考)

思考题 1

  因为 UTF-8 是变长编码,不同字符占用的字节数不同(英文常为 1 字节,汉字常为 3 字节)。所以“第 5 个字符”对应的字节位置并不固定,必须按编码规则逐字解析才能定位,不能直接按字节下标取。

思考题 2

  好处是内容永远不会被意外改动,可以放心共享、作为键、并发使用更安全。代价是任何“修改”都要新建一整串并复制内容,反复拼接或修改时开销大,因此常需要可变字符串来弥补。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪