字符串
复习
- 文字与编码:文字靠字符编码表示,ASCII、Unicode 与 UTF-8 解决编号与存储问题
- 数组:连续存储,可随机访问
- 抽象数据类型:先规定能做什么,再决定怎么做
TL;DR
- 字符串是字符的序列,底层常基于数组
- 编码决定了“一个字符占多少字节”
- 遍历、查找子串、切分、拼接是常见操作
- 有些语言里字符串不可变,安全便利,但有代价
正文
我们已经用过很多次字符串了。可它说到底,就是一种字符的序列——如果回想编码那一部分,字符又都是由二进制编号表示的。
底层其实就是数组
在大多数实现里,字符串的底层就是一个字符数组:字符一个挨一个地连续存放。
所以字符串天然继承了数组的特点:
- 可以按下标访问某个位置,
O(1) - 在中间插入或删除字符,需要挪动后面的部分,
O(n)
但这里有一个特别容易踩的坑:字符串的“第几个字符”和“第几个字节”,在有变长编码时并不一致。
比如 UTF-8 里,一个英文字母只占 1 个字节,可一个汉字往往占 3 个字节。于是“取第 5 个字符”,并不能简单地取第 5 个字节——必须按编码规则一路数过去。这正是编码知识会影响数据结构操作的一个典型例子。
常见操作
字符串上有一些高频操作:
- 遍历:逐个处理字符
- 查找子串:在一串里找另一串
- 切分:按分隔符拆成若干段
- 拼接:把两串接在一起
要留意拼接的代价。如果字符串底层是不可变的,那么每次拼接都可能需要新建一整串并复制内容,反复拼接会变得很慢。这也是为什么有些语言会提供专门的“可变字符串”类型,避免反复复制。
不可变:好处与代价
上文提到的“不可变”(immutable),是很多语言里字符串的重要特性:一旦创建,内容就不能被修改,只能生成新串。
它带来不少好处:
- 可以安全地在多处共享,不必担心被别人改掉
- 适合作为查找的键,因为内容不会变
- 并发使用时更省心
代价则是:任何“修改”都要生成新串,反复拼接或修改时开销较大。
这又是一次熟悉的权衡:用“不能改”换来安全与共享,也要为“每次改动都新建”付出代价。
思考题 1
在 UTF-8 字符串里,“第 5 个字符”为什么不能简单地取第 5 个字节?
思考题 2
字符串“不可变”有什么好处,又有什么代价?
小结
知识点
- 字符串是字符序列,底层常基于数组
- 变长编码下,字符位置与字节位置不一致
- 遍历、查找、切分、拼接是常见操作
- 不可变带来安全与共享,代价是修改要新建
参考资料
- Wikipedia(zh):字符串:由字符组成的序列
- Wikipedia(zh):UTF-8:一种变长的 Unicode 编码
思考题答案(仅供参考)
思考题 1
因为 UTF-8 是变长编码,不同字符占用的字节数不同(英文常为 1 字节,汉字常为 3 字节)。所以“第 5 个字符”对应的字节位置并不固定,必须按编码规则逐字解析才能定位,不能直接按字节下标取。
思考题 2
好处是内容永远不会被意外改动,可以放心共享、作为键、并发使用更安全。代价是任何“修改”都要新建一整串并复制内容,反复拼接或修改时开销大,因此常需要可变字符串来弥补。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪