前言
本篇文章主要淺析字符串\字符在 Swift 和 Objective-C 之間的區(qū)別及其簡單用法维咸。如有不妥的地方還望大家及時幫忙糾正。
字符串判空
在 swift 語言中空字符串初始化方式常用的有兩種:
// 方式一:
let testEmptyString0 = ""
// 方式二:
let testEmptyString1 = String()
在開發(fā)過程中,我們應(yīng)該如何用正確的方式來對字符串進(jìn)行判空處理呢?
// 方式一:這種方式其實(shí)就是判斷 characters.count 是否為0
if testEmptyString0.isEmpty {
// empty
}
// 方式二:
if testEmptyString0.characters.count {
// empty
}
// 方式三:
if (testEmptyString0 as NSString).length {
// empty
}
字符串長度計算
Objective-C
首先我們來回憶一下,在 Objective-C 中字符串是怎么計算長度的糕伐?我想大家都應(yīng)該知道砰琢。來看看蘋果是怎么說的:
A string object is implemented as an array of Unicode characters (in other words, a text string). An immutable string is a text string that is defined when it is created and subsequently cannot be changed. To create and manage an immutable string, use the NSString class. To construct and manage a string that can be changed after it has been created, use NSMutableString.
A string object presents itself as an array of Unicode characters. You can determine how many characters it contains with the length method and can retrieve a specific character with the characterAtIndex: method.
看完這段話蘸嘶,想必大家都明白 NSString 是怎么實(shí)現(xiàn)的,以及如何獲取其長度陪汽。通過 length 方法即可训唱,那么 length 方法是如何實(shí)現(xiàn)的呢?蘋果官方是這樣說的:length 方法利用的是 UTF-16 表示的十六位編碼單元數(shù)字為單位進(jìn)行計算的(The number of UTF-16 code units in the receiver.)挚冤。UTF-16是什么况增?(感興趣的童鞋可以看一下我之前寫的一篇文章,字符編碼(一))训挡,此處不再詳述澳骤。
Swift 3.0
Unicode 標(biāo)量表示
在 Swift 中,字符和字符串都是基于 Unicode 標(biāo)量建立的澜薄,采用21位二進(jìn)制進(jìn)行編碼为肮,共17個平面(除了基本多文種平面中的 UTF-16 代理對碼位外,即U+D800至U+DFFF的編碼空間)肤京,也就是說編碼范圍是U+0000-U+D7FFF 或者 U+E000-U+10FFFF颊艳。
A Unicode scalar is any Unicode code point in the range U+0000 to U+D7FF inclusive or U+E000 to U+10FFFF inclusive. Unicode scalars do not include the Unicode surrogate pair code points, which are the code points in the range U+D800 to U+DFFF inclusive.”
因此在 Swift 中,我們可直接采用 Unicode 標(biāo)量的形式來表示字符或字符串忘分,如:
let tingC = "\u{542C}" // 聽
let xinC = "\u{5FC3}" // 心
可擴(kuò)展的字形群集(簇)
在 Swift 中棋枕,每一個 Character 類型實(shí)例都代表單個可擴(kuò)展的字形群集——即由一個或多個 Unicode 標(biāo)量的序列組成的一個可讀字符。
漢字 “聽” 拼音為 tīng妒峦,以字母 ī 為例重斑,用兩種方式表示。第一種肯骇,可以直接用單個 Unicode 標(biāo)量 ī (LATIN SMALL LETTER I WITH MACRON) 來表示窥浪,即 U+012B卤恳,該字形群集中包含一個 Unicode 標(biāo)量。第二種寒矿,可以采用兩個 Unicode 標(biāo)量來表示突琳,一個拉丁字母 i (LATIN SMALL LETTER I) 加上一個音調(diào)符(元音,COMBINING MACRON ACCENT)的標(biāo)量符相,即 U+0069 U+0304拆融,這樣,當(dāng)字母 i 被 Unicode 文字渲染系統(tǒng)時就會轉(zhuǎn)換成 ī啊终,該字形群集中包含兩個 Unicode 標(biāo)量镜豹。
let tingO = "t" + "\u{0069}" + "ng" // Prints "ting "
let tingPS = "t" + "\u{0069}" + "\u{0304}" + "ng" // Prints "tīng"
let tingPD = "t" + "\u{012B}" + "ng" // Prints "tīng"
這兩種情況中,字母 ī 即代表了 Swift 中單個 Character 類型實(shí)例蓝牲,也代表了一個可擴(kuò)展的字形群集趟脂。想了解更多關(guān)于可擴(kuò)展的字形群集,可參考此鏈接例衍。
字符串長度
我們已經(jīng)簡單了解了可擴(kuò)展的字形群集昔期,現(xiàn)在我們再來看看 Swift 字符串中一些有意思的事。
Swift 中 String 類型佛玄,說白了就是 Character 類型實(shí)例的集合硼一,在開發(fā)過程中,我們一般采用兩種方式來求字符串的長度梦抢,第一種是轉(zhuǎn)成 Objective-C 中的 NSString 類型般贼,通過 length 方法來獲取其長度,第二種是通過字符串屬性 characters.count 的方式獲得奥吩。本小節(jié)主要討論第二種哼蛆,本文會在結(jié)尾針對這兩種方式進(jìn)行比較。
在 Swift 中霞赫,細(xì)心的同學(xué)或許已經(jīng)發(fā)現(xiàn) tingPD 與 tingPS 字符串的字符數(shù)量是一樣的:
print("tingPD-Count:\(tingPD.characters.count), tingPS-Count:\(tingPS.characters.count)")
// Prints "tingPD-Count:4, tingPS-Count:4"
下面我們來解決此疑惑腮介,筆者已在前文說過,Swift 中 String\Character 都是基于 Unicode 標(biāo)量建立的绩脆,且 String 是 Character 的集合(即包含關(guān)系)萤厅,而 String 屬性 characters.count 其實(shí)就是計算 Character 的數(shù)量,那么 character 是怎么定義的呢靴迫,或者說什么才算是一個 character惕味?此時又引出了一個概念——字形群集界限(Grapheme Cluster Boundaries),而”什么才算是一個 character玉锌?“這個問題就是字形群集界限給出的答案名挥,想深入了解的同學(xué)請看:傳送門。從用戶感觀(user-perceived)角度講主守,不管是字符 ī(U+012B) 或者是 i(U+0069) 再加上一個音調(diào)符(U+0304)禀倔,這兩種表示最終的結(jié)果都是組成一個相同的可讀的字符榄融,因此 tingPD 與 tingPS 字符串中的字符數(shù)量是一樣的。
通過上文的簡單解釋救湖,可以得出兩個結(jié)論:
一個字符串拼接一個字符時愧杯,不一定會更改字符串的數(shù)量,即 characters.count 的值鞋既。
在沒有獲取到字形群集界限的時候力九,無法計算出該字符串的字符數(shù)量,因此必須遍歷字符串中全部的 Unicode 標(biāo)量以獲取字形群集界限邑闺,進(jìn)而確定字符串的字符數(shù)量跌前。
下面在看一個例子,相信大家都已明白輸出結(jié)果的原因:
var iWord = "i"
print("iword-Count: \(iWord.characters.count)")
// Prints "iword-Count: 1"
iWord += "\u{0304}" // ī
print("iword-Count: \(iWord.characters.count)")
// Prints "iword-Count: 1"
.length 與 .characters.count 的區(qū)別
首先 .length 是 Objective-C 中字符串長度計算方法陡舅,而 .characters.count 可以說是 Swift 中字符串長度計算方法抵乓,由于 Swift 中 String 類型可以轉(zhuǎn)成 Objective-C 中的 NSString 類型,因此在 Swift 開發(fā)過程中可能有如下兩種寫法:
print("tingPS.characters.count")
// Prints "4"
print("(tingPS as NSString).length")
// Prints "5"
從上述結(jié)果可看出靶衍,.length 方法得到的字符串長度為5灾炭,而 .characters.count 等于4,可能讀者會有點(diǎn)懵摊灭,同一個字符串怎么計算的長度不一致咆贬?其實(shí) .length 與 .characters.count 的計算原理在上文已經(jīng)做了解釋,本小節(jié)就簡單總結(jié)一下:
.length 與 .characters.count 返回值不總是相同的帚呼,.length 方法是采用 UTF-16 表示的編碼單元為單位進(jìn)行計算并返回的,即字母 i(U+0069) 皱蹦、音調(diào)符(U+0304)會當(dāng)做兩個字符煤杀,因而長度為2。.character.count 的值是通過字形群集界限來確定字符數(shù)量的沪哺,如還不理解請查看上文沈自。(PS:其實(shí)這里也是 Swift 中采用索引的方式訪問字符串的原因)