Swift Standard Library Reference 主体为对该链接 String
部分理解基础上的翻译,但有较大改动且加入更多个人见解与扩展内容
Unicode and you by BetterExplained BetterExplained对于Unicode的解释,随手找的
?? ~ emoji unicode characters ~ ??
所有我不能确定标准与否的术语翻译,均会在第一次出现时于括号内标注原文。
可能稍显啰嗦,我是希望能够借助尽量 细致、直观、全面、有理有据 的讲解,来提升自己的理解,相信也能帮到其他人。我很喜欢读这种态度的文章,也希望自己的文章能有这种水平,希望你也喜欢。
Swift 标准库提供了 String
文本类型,适用 Unicode 文本。本文内容就是,如何对它进行定位(index)和切分(slice)。
先看下面的例子
let str = "Héllo, ??laygr?und!"
let badRange = 4...12
// str[badRange]
// 取消上行的注释将会看到一个错误 “Subscript is unavailable: cannot subscript String with a range of Int”
不能用 Int
定义的 Range
范围来取子串,为毛? 字符串的第 n 个位置存第 n 个字 这样的逻辑有什么问题?为了理解 Swift 这样设计的目的,下面要简单扯下字符集。
C语言的字符串是这样的
01000001 | 01000010 | 01000011 | 01000100 |
---|---|---|---|
A | B | C | D |
一个字节存一个字符,第 n 个字节存第 n 个字符,没问题。但是 Unicode 可以表示的字符很多,一个字节表示不完。于是要用更多字节表示一个字符,但 ASCII 中 ABCD 这些字符只要一个字节就够了,在这里也要统一用多个字节就会造成浪费。因此有了变长编码如 UTF-8 ,一些字符用一字节表示,另一些用多个字节。如字符串 "A??" ,utf-8的表示如下
01000001 | 11110000 …这里省略6个字节,呵呵呵… 10111000 |
---|---|
A | ?? |
一个有趣的细节: UTF-8 的 “A” 和 前面 ASCII 的 “A” 编码一致,都是 65 。实际上不止是 “A” ,UTF-8 是兼容 ASCII 的,所有 ASCII 内的字符的在 UTF-8 和 ASCII 中的表示都一样,也即都是占一个字节
另一个有趣的细节。。。: UTF-8 一个字符使用的最多是 4 个字节而不是 8 个,“??” 符号其实是由两个地区标记符(regional indicator symbol letter)“u” 和 “s” 拼起来的,所以才用了 8 字节。 这里的地区标记符 “u” 和 “s” 不是英文字母,是专门用来拼装国家、地区标记的特殊字符。从它们占用了 4 个字节而不是 ASCII 的 1 字节这里也可以看出来区别
表示 “A” 只用一个字节,表示 “??” 却用了足足八个字节。这就破坏了上表中字节和字符一一对应的关系,数据结构中的第 n 位和字符串的第 n 个字符之间的对应关系没了。
前面我们说过 Swift 标准库提供的 String
用的是 Unicode ,现在再回去看前面那句报错 Subscript is unavailable: cannot subscript String with a range of Int
就知道了。不能用 Int 指定的范围来定位、切分字符串的原因就是因为,由于使用了变长编码,导致 String
的数据结构的第 n 个元素,不是我们要的第 n 个字符。(姑且先这么说吧)
String
中要定位、切分字符串,需要使用 String.Index
对象提供的一系列方法,它们会确保操作以字符为单位进行,不会出现让你把一个多字节字符砍成两半的问题:
// successor() 下一个字符
str[str.startIndex] // "H"
str[str.startIndex.successor()] // "é"
str[str.startIndex.successor().successor()] // "l"
// predecessor() 上一个字符
str[str.endIndex.predecessor()] // "!"
str[str.endIndex.predecessor().predecessor()] // "d"
// advancedBy(Int) 与按给定次数执行前两个方法效果相同
str[str.startIndex.advancedBy(7)] // 与执行七次 successor() 效果相同 "??"
str[str.endIndex.advancedBy(-7)] // 与执行七次 predecessor() 效果相同 "g"
// 此处创建的 Range 是 Range<Index> 类型,而非 Range<Int>
let range = str.startIndex.advancedBy(4)...str.startIndex.advancedBy(12)
str[range] // "o, ??laygr"
上面是相关的常用用法。
至此,这部分的学习就暂时告一段落了,关于 String 的其它内容,以后遇到再谈。