iOS一点点 - Swift 标准库中的 String
参考资料
Swift Standard Library Reference 主体为对该链接 String 部分理解基础上的翻译,但有较大改动且加入更多个人见解与扩展内容
Unicode and you by BetterExplained BetterExplained对于Unicode的解释,随手找的
:imp::dog: ~ emoji unicode characters ~ :fire::angel:所有我不能确定标准与否的术语翻译,均会在第一次出现时于括号内标注原文。
可能稍显啰嗦,我是希望能够借助尽量 细致、直观、全面、有理有据 的讲解,来提升自己的理解,相信也能帮到其他人。我很喜欢读这种态度的文章,也希望自己的文章能有这种水平,希望你也喜欢。
正文
Swift 标准库提供了 String 文本类型,适用 Unicode 文本。本文内容就是,如何对它进行定位(index)和切分(slice)。
先看下面的例子
let str = "Héllo, :us:laygr:open_mouth: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:us:" ,utf-8的表示如下
01000001 | 11110000 …这里省略6个字节,呵呵呵… 10111000 |
---|---|
A | :us: |
一个有趣的细节:
UTF-8 的 “A” 和 前面 ASCII 的 “A” 编码一致,都是 65 。实际上不止是 “A” ,UTF-8 是兼容 ASCII 的,所有 ASCII 内的字符的在 UTF-8 和 ASCII 中的表示都一样,也即都是占一个字节
另一个有趣的细节。。。:
UTF-8 一个字符使用的最多是 4 个字节而不是 8 个,“:us:” 符号其实是由两个地区标记符(regional indicator symbol letter)“u” 和 “s” 拼起来的,所以才用了 8 字节。
这里的地区标记符 “u” 和 “s” 不是英文字母,是专门用来拼装国家、地区标记的特殊字符。从它们占用了 4 个字节而不是 ASCII 的 1 字节这里也可以看出来区别
表示 “A” 只用一个字节,表示 “:us:” 却用了足足八个字节。这就破坏了上表中字节和字符一一对应的关系,数据结构中的第 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() 效果相同 ":us:" 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, :us:laygr"
上面是相关的常用用法。
至此,这部分的学习就暂时告一段落了,关于 String 的其它内容,以后遇到再谈。
</div>