盡管語法簡易,但String類型是一種快速、現(xiàn)代化的字符串實現(xiàn)。 每一個字符串都是由編碼無關(guān)的 Unicode 字符組成,并支持訪問字符的多種 Unicode 表示形式(representations)。
字符串可變性 (String Mutability)
您可以通過將一個特定字符串分配給一個變量來對其進行修改,或者分配給一個常量來保證其不會被修改:
var variableString = "Horse"
variableString += " and carriage"
// variableString 現(xiàn)在為 "Horse and carriage"
let constantString = "Highlander"
constantString += " and another Highlander"
// 這會報告一個編譯錯誤 (compile-time error) - 常量字符串不可以被修改。
字符串是值類型(Strings Are Value Types)
Swift 的String類型是值類型。 如果您創(chuàng)建了一個新的字符串,那么當其進行常量、變量賦值操作,或在函數(shù)/方法中傳遞時,會進行值拷貝。 任何情況下,都會對已有字符串值創(chuàng)建新副本,并對該新副本進行傳遞或賦值操作。
字符串插值 (String Interpolation)
字符串插值是一種構(gòu)建新字符串的方式,可以在其中包含常量、變量、字面量和表達式。 您插入的字符串字面量的每一項都在以反斜線為前綴的圓括號中:
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"
Unicode 標量(Unicode Scalars)
Swift 的String類型是基于 Unicode 標量 建立的。 Unicode 標量是對應(yīng)字符或者修飾符的唯一的21位數(shù)字,例如U+0061表示小寫的拉丁字母(LATIN SMALL LETTER A)("a"),U+1F425表示小雞表情(FRONT-FACING BABY CHICK) ("??")。
ps:Unicode
碼位(code poing)的范圍是U+0000到U+D7FF或者U+E000到U+10FFFF。Unicode 標量不包括 Unicode代理項(surrogate pair)碼位,其碼位范圍是U+D800到U+DFFF
注意不是所有的21位 Unicode 標量都代表一個字符,因為有一些標量是留作未來分配的。已經(jīng)代表一個典型字符的標量都有自己的名字,例如上面例子中的LATIN SMALL LETTER A和FRONT-FACING BABY CHICK。
字符串字面量的特殊字符 (Special Characters in String Literals)
字符串字面量可以包含以下特殊字符:
- 轉(zhuǎn)義字符
\0(空字符)、\\(反斜線)、\t(水平制表符)、\n(換行符)、\r(回車符)、\"(雙引號)、\'(單引號)。 - Unicode 標量,寫成
\u{n}(u為小寫),其中n為任意一到八位十六進制數(shù)且可用的 Unicode 位碼。
可擴展的字形群集(Extended Grapheme Clusters)
每一個 Swift 的Character類型代表一個可擴展的字形群。 一個可擴展的字形群是一個或多個可生成人類可讀的字符 Unicode 標量的有序排列。 舉個例子,字母é可以用單一的 Unicode 標量é(LATIN SMALL LETTER E WITH ACUTE, 或者U+00E9)來表示。然而一個標準的字母e(LATIN SMALL LETTER E或者U+0065) 加上一個急促重音(COMBINING ACTUE ACCENT)的標量(U+0301),這樣一對標量就表示了同樣的字母é。 這個急促重音的標量形象的將e轉(zhuǎn)換成了é。
在這兩種情況中,字母é代表了一個單一的 Swift 的Character值,同時代表了一個可擴展的字形群。 在第一種情況,這個字形群包含一個單一標量;而在第二種情況,它是包含兩個標量的字形群:
let eAcute: Character = "\u{E9}" // é
let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ?
// eAcute 是 é, combinedEAcute 是 é
可擴展的字符群集是一個靈活的方法,用許多復雜的腳本字符表示單一的Character值。 例如,來自朝鮮語字母表的韓語音節(jié)能表示為組合或分解的有序排列。 在 Swift 都會表示為同一個單一的Character值:
let precomposed: Character = "\u{D55C}" // ?
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ?, ?, ?
// precomposed 是 ?, decomposed 是 ?
可拓展的字符群集可以使包圍記號(例如COMBINING ENCLOSING CIRCLE或者U+20DD)的標量包圍其他 Unicode 標量,作為一個單一的Character值:
let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute 是 é?
局部的指示符號的 Unicode 標量可以組合成一個單一的Character值,例如REGIONAL INDICATOR SYMBOL LETTER U(U+1F1FA)和REGIONAL INDICATOR SYMBOL LETTER S(U+1F1F8):
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS 是 ????
計算字符數(shù)量 (Counting Characters)
如果想要獲得一個字符串中Character值的數(shù)量,可以使用字符串的characters屬性的count屬性
注意在 Swift 中,使用可拓展的字符群集作為Character值來連接或改變字符串時,并不一定會更改字符串的字符數(shù)量。
例如,如果你用四個字符的單詞cafe初始化一個新的字符串,然后添加一個COMBINING ACTUE ACCENT(U+0301)作為字符串的結(jié)尾。最終這個字符串的字符數(shù)量仍然是4,因為第四個字符是é,而不是e
ps:可擴展的字符群集可以組成一個或者多個 Unicode 標量。這意味著不同的字符以及相同字符的不同表示方式可能需要不同數(shù)量的內(nèi)存空間來存儲。所以 Swift 中的字符在一個字符串中并不一定占用相同的內(nèi)存空間數(shù)量。因此在沒有獲取字符串的可擴展的字符群的范圍時候,就不能計算出字符串的字符數(shù)量。如果您正在處理一個長字符串,需要注意
characters屬性必須遍歷全部的 Unicode 標量,來確定字符串的字符數(shù)量。
另外需要注意的是通過characters屬性返回的字符數(shù)量并不總是與包含相同字符的
NSString的length屬性相同。NSString的length屬性是利用UTF-16表示的十六位代碼單元數(shù)字,而不是 Unicode 可擴展的字符群集。作為佐證,當一個NSString的length屬性被一個Swift的String值訪問時,實際上是調(diào)用了utf16Count。
字符串索引 (String Indices)
每一個String值都有一個關(guān)聯(lián)的索引(index)類型,String.Index,它對應(yīng)著字符串中的每一個Character的位置。
前面提到,不同的字符可能會占用不同數(shù)量的內(nèi)存空間,所以要知道Character的確定位置,就必須從String開頭遍歷每一個 Unicode 標量直到結(jié)尾。因此,Swift 的字符串不能用整數(shù)(integer)做索引。
使用startIndex屬性可以獲取一個String的第一個Character的索引。使用endIndex屬性可以獲取最后一個Character的后一個位置的索引。因此,endIndex屬性不能作為一個字符串的有效下標。如果String是空串,startIndex和endIndex是相等的。
通過調(diào)用String.Index的predecessor()方法,可以立即得到前面一個索引,調(diào)用successor()方法可以立即得到后面一個索引。任何一個String的索引都可以通過鎖鏈作用的這些方法來獲取另一個索引,也可以調(diào)用advancedBy(_:)方法來獲取。但如果嘗試獲取出界的字符串索引,就會拋出一個運行時錯誤。
你可以使用下標語法來訪問String特定索引的Character。
let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.endIndex.predecessor()]
// !
greeting[greeting.startIndex.successor()]
// u
let index = greeting.startIndex.advancedBy(7)
greeting[index]
// a
使用characters屬性的indices屬性會創(chuàng)建一個包含全部索引的范圍(Range),用來在一個字符串中訪問單個字符。
for index in greeting.characters.indices {
print("\(greeting[index]) ", terminator: " ")
}
// 打印輸出 "G u t e n T a g !"
字符串/字符相等 (String and Character Equality)
字符串/字符可以用等于操作符(==)和不等于操作符(!=)
如果兩個字符串(或者兩個字符)的可擴展的字形群集是標準相等的,那就認為它們是相等的。在這個情況下,即使可擴展的字形群集是有不同的 Unicode 標量構(gòu)成的,只要它們有同樣的語言意義和外觀,就認為它們標準相等。
例如,LATIN SMALL LETTER E WITH ACUTE(U+00E9)就是標準相等于LATIN SMALL LETTER E(U+0065)后面加上COMBINING ACUTE ACCENT(U+0301)。這兩個字符群集都是表示字符é的有效方式,所以它們被認為是標準相等的:
// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"
// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"
if eAcuteQuestion == combinedEAcuteQuestion {
print("These two strings are considered equal")
}
// 打印輸出 "These two strings are considered equal"
相反,英語中的LATIN CAPITAL LETTER A(U+0041,或者A)不等于俄語中的CYRILLIC CAPITAL LETTER A(U+0410,或者A)。兩個字符看著是一樣的,但卻有不同的語言意義:
let latinCapitalLetterA: Character = "\u{41}"
let cyrillicCapitalLetterA: Character = "\u{0410}"
if latinCapitalLetterA != cyrillicCapitalLetterA {
print("These two characters are not equivalent")
}
// 打印 "These two characters are not equivalent"
注意: 在 Swift 中,字符串和字符并不區(qū)分區(qū)域。
前綴/后綴相等 (Prefix and Suffix Equality)
通過調(diào)用字符串的hasPrefix(_:)/hasSuffix(_:)方法來檢查字符串是否擁有特定前綴/后綴,兩個方法均接收一個String類型的參數(shù),并返回一個布爾值。
注意: hasPrefix(:)和hasSuffix(:)方法都是在每個字符串中逐字符比較其可擴展的字符群集是否標準相等
字符串的 Unicode 表示形式(Unicode Representations of Strings)
當一個 Unicode 字符串被寫進文本文件或者其他儲存時,字符串中的 Unicode 標量會用 Unicode 定義的幾種編碼格式編碼。每一個字符串中的小塊編碼都被稱為代碼單元。這些包括 UTF-8 編碼格式(編碼字符串為8位的代碼單元), UTF-16 編碼格式(編碼字符串位16位的代碼單元),以及 UTF-32 編碼格式(編碼字符串32位的代碼單元)。
Swift 提供了幾種不同的方式來訪問字符串的 Unicode 表示形式。 您可以利用for-in來對字符串進行遍歷,從而以 Unicode 可擴展的字符群集的方式訪問每一個Character值。
另外,能夠以其他三種 Unicode 兼容的方式訪問字符串的值:
- UTF-8 代碼單元集合 (利用字符串的
utf8屬性進行訪問) - UTF-16 代碼單元集合 (利用字符串的
utf16屬性進行訪問) - 21位的 Unicode 標量值集合,也就是字符串的 UTF-32 編碼格式 (利用字符串的
unicodeScalars屬性進行訪問)
要點總結(jié)
Swift的character的類型是可擴展的字型集,因此character所占的空間是不一定相等的,這也就是對String類型的變量進行操作時不能使用Int下標來獲取字符或者截取字符串的原因所在!