iOS一點點 - Swift 標準庫中的 String

jopen 8年前發布 | 8K 次閱讀 IOS Swift Apple Swift開發

參考資料

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

來自: http://segmentfault.com/a/1190000004222162

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!