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 的其它內容,以后遇到再談。