字符串和字符 (Strings and Characters)
自從蘋(píng)果2014年發(fā)布Swift,到現(xiàn)在已經(jīng)兩年多了,而Swift也來(lái)到了3.1版本。去年利用工作之余,共花了兩個(gè)多月的時(shí)間把官方的Swift編程指南看完。現(xiàn)在整理一下筆記,回顧一下以前的知識(shí)。有需要的同學(xué)可以去看官方文檔>>。
字符串字面值 (String Literals)
使用字符串字面值來(lái)初始化一個(gè)常量或者變量:
let someString = "Some string literal value"
Swift根據(jù)字面值來(lái)推斷出someString
是String
類(lèi)型。
初始化一個(gè)空字符串
為了創(chuàng)建更長(zhǎng)的字符串,我們通常要先初始化一個(gè)空字符串,我們可以使用下面兩種方法:
var emptyString = "" // 空字符串字面值
var anotherEmptyString = String() // 使用默認(rèn)構(gòu)造函數(shù)
// 這兩個(gè)字符串都是空的,并且是相等的
通過(guò)檢查字符串的布爾類(lèi)型屬性isEmpty
來(lái)判斷一個(gè)字符串是否為空:
if emptyString.isEmpty {
print("這里什么都沒(méi)有")
}
// Prints "這里什么都沒(méi)有"
字符串的可變性 (String Mutability)
使用let
聲明一個(gè)不可變的字符串,用var
聲明一個(gè)可變的字符串:
var variableString = "Horse"
variableString += "and carriage"
// variableString 現(xiàn)在是 "Horse and carriage"
let constatString = "Highlander"
constantString += "and another Highlander"
// 會(huì)產(chǎn)生一個(gè)編譯錯(cuò)誤,因?yàn)閏onstantString是一個(gè)常量,不能被改變
在OC中,需要使用NSString
和NSMutableString
來(lái)分別定義不可變和可變字符串。
字符串是值類(lèi)型 (Strings Are Value Types)
Swift的String
類(lèi)型是值類(lèi)型。如果我們創(chuàng)建一個(gè)新的字符串,那么把這個(gè)字符串以復(fù)制的形式傳給一個(gè)函數(shù)或方法,或者把它賦值給另外一個(gè)常量或常量。
在Swift的底層實(shí)現(xiàn),Swift的編譯器會(huì)優(yōu)化字符串的使用,在有必要的時(shí)候才會(huì)進(jìn)行真正的復(fù)制,這就意味著把字符串作為值類(lèi)型在使用的時(shí)候,都有非常好的性能。
與字符交互 (Working with Characters)
使用for-in
循環(huán)遍歷字符串的characters
屬性來(lái)訪(fǎng)問(wèn)單個(gè)字符:
for character in "Dog!??".characters {
print(character)
}
// D
// o
// g
// !
// ??
字符串可以通過(guò)傳遞一個(gè)字符數(shù)組給String
的構(gòu)造函數(shù)來(lái)初始化:
let catCharacters: [Character] = ["C", "a", "t", "!", "??"]
let catString = String(catCharacters)
print(catString)
// Prints "Cast!??"
連接字符串和字符 (Concatenating Strings and Characters)
使用加號(hào)運(yùn)算符(+
)來(lái)拼接兩個(gè)字符串:
let string1 = "hello"
let string2 = "there"
let welcome = string1 + string2
// welcome 等于 "hello there"
使用加法賦值運(yùn)算符來(lái)拼接一個(gè)已經(jīng)存在字符串:
var instruction = "look over"
instruction += string2
// instruction 等于 "look over there"
使用String
類(lèi)型的append()
方法來(lái)拼接:
let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome 等于 "hello there!"
注意:不能添加一個(gè)String
或者Character
到一個(gè)已經(jīng)存在的Character
變量,因?yàn)?code>Character類(lèi)型的值只能包含一個(gè)字符。
字符串插值 (String Interpolation)
字符串插值可以把常量、變量、字面值和其他表達(dá)式混合在一起,拼接成一個(gè)新的字符串。
let multiplier = 3
let message = "\(multiplier) 乘以 2.5 等于 \(Double(multiplier) * 2.5)"
// message is "3 乘以 2.5 等于 7.5"
Unicode
Unicode是在不同系統(tǒng)中編碼、展示和處理文本的國(guó)際標(biāo)準(zhǔn)。可以讓我們以國(guó)際化的形式在不同的語(yǔ)言中展示幾乎任何字符,從外部文件(例如text文件或網(wǎng)頁(yè))讀取那些字符或者把那些字符寫(xiě)到外部文件。Swift的String
和Character
類(lèi)型是完全兼容Unicode的。
Unicode標(biāo)量 (Unicode Scalars)
在底層,Swift的原生String
類(lèi)型是從Unicode標(biāo)量值建立的。一個(gè)Unicode標(biāo)量是一個(gè)字符或者修飾符唯一的21-bit數(shù)字,例如U+0061
是LATIN SMALL LETTER A
("a"
),U+1F425
是FRONT-FACING BABY CHICK
("??"
)。
注意:一個(gè)Unicode標(biāo)量是在U+0000
到U+D7FF
(包含首尾)這個(gè)范圍中的一個(gè)代碼點(diǎn)(code point),或者是U+E000
到U+10FFFF
(包含首尾)這個(gè)范圍的一個(gè)代碼點(diǎn)。Unicode標(biāo)量不包含Unicode代理對(duì)(surrogate pair)代碼點(diǎn),代理對(duì)的代碼點(diǎn)范圍是U+D800
到U+DFFF
(包含首尾)。
并不是所有的21-bitUnicode標(biāo)量都賦值給了一個(gè)字符,有一些標(biāo)量是保留起來(lái)給未來(lái)使用的。被賦值給字符串的Unicode標(biāo)量都有一個(gè)名字,例如上面例子中的LATIN SMALL LETTER A
和FRONT-FACING BABY CHICK`。
字符串字面值中的特殊字符 (Special Characters in String Literals)
字符串字面值可以包含以下字符:
- 轉(zhuǎn)義字符:
\0
(空字符)、\\
(反斜杠)、\t
(水平制表符)、\n
(換行)、\r
(回車(chē))、\"
(雙引號(hào))、\'
(單引號(hào)) - 一個(gè)任意的Unicode標(biāo)量,寫(xiě)成這個(gè)樣式
\u{n}
,n是1~8的十六進(jìn)制數(shù)字并且等于一個(gè)有效的Unicode代碼點(diǎn)
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein
let dollarSign = "\u{24}" // $, Unicode scalar U+0024
let blackHeart = "\u{2665}" // ?, Unicode scalar U+2665
let sparklingHeart = "\u{1F496}" // ??, Unicode scalar U+1F496
擴(kuò)展字形集群 (Extended Grampheme Clusters)
每個(gè)SwiftCharacter
類(lèi)型實(shí)例代表著一個(gè)擴(kuò)展字形集群。一個(gè)擴(kuò)展字形集群是一系列的一個(gè)或多個(gè)Unicode標(biāo)量,并形成一個(gè)人類(lèi)能夠讀懂的字符。
例如,字母é
代表這個(gè)Unicode標(biāo)量é
(LATIN SMALL LETTER E WITH ACUTE
, 或者 U+00E9
)。然而這個(gè)字母還可以代表一對(duì)標(biāo)量:一個(gè)標(biāo)準(zhǔn)的字母e
(LATIN SMALL LETTER
,或者U+0065
),接著是COMBINING ACUTE ACCENT
標(biāo)量(U+0301
)。COMBINING ACUTE ACCENT
標(biāo)量被圖形化的應(yīng)用于在它前面的標(biāo)量,Unicode文本渲染系統(tǒng)把e
渲染成é
。
let eAcute: Character = "\u{E9}" // é
let combinedEAcute: Character = "\u{65}\u{301}" // e followed by ?
// eAcute is é, combinedEAcute is é
擴(kuò)展字形集群是一個(gè)能把很多復(fù)雜腳本字符顯示為一個(gè)Character
字符的一種靈活的方式。例如,韓文字母的韓文音節(jié)可以用合成或者分解序列來(lái)表示:
let precomposed: Character = "\u{D55C}" // ?
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ?, ?, ?
// precomposed is ?, decomposed is ?
擴(kuò)展字形集群還支持封閉標(biāo)志(例如COMBINING ENCLOSING CIRCLE
,或者U+20DD
):
let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute is é?
代表區(qū)域標(biāo)志符號(hào)的Unicode標(biāo)量還可以結(jié)成對(duì),形成一個(gè)Character
字符。例如這個(gè)組合:REGIONAL INDICATOR SYMBOL LETTER U (U+1F1FA)
和REGIONAL INDICATOR SYMBOL LETTER S (U+1F1F8)
:
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS is ????
Counting Characters
字符計(jì)數(shù) (Counting Characters)
使用字符串的characters
的count
屬性來(lái)后去字符的個(gè)數(shù):
let unusualMenagerie = "Koala ??, Snail ??, Penguin ??, Dromedary ??"
print("unusualMenagerie has \(unusualMenagerie.characters.count) characters")
// Prints "unusualMenagerie has 40 characters"
注意:在Swift中,使用擴(kuò)展字形集群形成的字符,在拼接和修改字符串時(shí)不一定會(huì)影響字符的個(gè)數(shù)。例如:初始化一個(gè)字符串cafe
,接著在后面追加COMBINING ACUTE ACCENT (U+0301)
,最終得到的字符串還是只有4個(gè)字符,第四個(gè)字符是é
,不是e
:
var word = "cafe"
print("the number of characters in \(word) is \(word.characters.count)")
// Prints "the number of characters in cafe is 4"
word += "\u{301}" // COMBINING ACUTE ACCENT, U+0301
print("the number of characters in \(word) is \(word.characters.count)")
// Prints "the number of characters in café is 4"
注意:擴(kuò)展字形集群可以有一個(gè)或多個(gè)Unicode標(biāo)尺組成,這意味著不同的字符和同一個(gè)字符的不同展示方式需要不同大小的內(nèi)存來(lái)存儲(chǔ)。所以,Swift中的字符不會(huì)在字符串的展示中占用相同的內(nèi)存量。那么,如果不遍歷一個(gè)字符串,就無(wú)法知道這個(gè)字符串的字符數(shù),從而無(wú)法確定字符的擴(kuò)展字形集群邊界。如果我們要使用特別長(zhǎng)的字符串,必須清楚地知道字符串的characters
屬性必須遍歷整個(gè)字符串的全部Unicode標(biāo)量來(lái)決定字符串的所有字符。
在擁有相同字符得到情況下,String
的characters
屬性返回的字符數(shù)不總是等于NSString
的length
屬性。NSString
的長(zhǎng)度是基于在字符串的UTF-16顯示之內(nèi)的16-bit代碼單元的數(shù)字,而不是基于在字符串之內(nèi)的Unicode擴(kuò)展字形集群的數(shù)字。
訪(fǎng)問(wèn)和修改字符串 (Accessing and Modifying a String)
字符串索引 (String Indices)
每個(gè)String
都有一個(gè)關(guān)聯(lián)的索引類(lèi)型,String.Index
,對(duì)應(yīng)著字符串里的沒(méi)一個(gè)字符的位置。
就像上面說(shuō)到的,不同的字符需要占用不用的內(nèi)存量,所以為了得到字符的位置,我們需要遍歷整個(gè)字符串的全部Unicode標(biāo)量。所以Swift的字符串不能按整數(shù)值進(jìn)行索引。
使用startIndex
來(lái)訪(fǎng)問(wèn)字符串的第一個(gè)字符的位置,endIndex
是字符串最后一個(gè)字符的下一個(gè)位置。所以,endIndex
不是一個(gè)有效的字符串索引。如果一個(gè)字符串是空的,那么startIndex
和endIndex
是相等的。
使用index(before:)
和index(after:)
來(lái)訪(fǎng)問(wèn)索引;使用index(_:offsetBy:)
來(lái)訪(fǎng)問(wèn)離一個(gè)給定的索引一定距離的索引。
let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before:greeting.endIndex)]
// !
greeting[greeting.index(after:greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
// a
下面兩行代碼將會(huì)報(bào)錯(cuò),已經(jīng)超出了字符串的范圍:
greeting[greeting.endIndex] // Error
greeting.index(after:greeting.endIndex) Error
使用characters
的indices
屬性來(lái)訪(fǎng)問(wèn)字符串中每一個(gè)字符的索引:
for index in greeting.characters.indices {
print("\(greeting[index]) ", terminator: "")
}
// Prints "G u t e n T a g ! "
注意:我們可以在遵循了Collection
協(xié)議的任何類(lèi)型中使用startIndex
/endIndex
屬性和index(before:)
/index(after:)
/index(_:offsetBy:)
方法,包括:String
、Array
、Dictionary
、Set
。
插入和移除 (Inserting and Removing)
在特定的位置插入一個(gè)字符,使用insert(_:at:)
;在特定的位置插入另外一個(gè)字符串內(nèi)容,使用insert(contentsOf:at:)
:
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome 現(xiàn)在是 hello!
welcome.insert(contentsOf: " there".characters, at: welcome.index(before: welcome.endIndex))
// welcome 現(xiàn)在是 hello there!
在特定的位置刪除一個(gè)字符,使用remove(at:)
,刪除一個(gè)范圍內(nèi)的字符串,使用removeSubrange(_:)
:
welcome.remove(at: welcome.index(before:welcome.endIndex))
// welcome 現(xiàn)在是 hello there
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome 現(xiàn)在是 hello
注意:我們可以在遵循了RangeReplaceableCollection
協(xié)議的任何類(lèi)型中使用insert(_:at:)
、insert(contentsOf:at:)
、remove(at:)
和removeSubrange(_:)
方法,包括:String
、Array
、Dictionary
、Set
。
字符串的比較 (Comparing Strings)
Swift提供了三種方式來(lái)進(jìn)行文本比較:1)字符串和字符相等;2)前綴相等;3)后綴相等。
字符串和字符相等 (String and Character Equality)
使用等于==
和不等于!=
進(jìn)行比較:
let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
print("These two strings are considered equal")
}
// Prints "These two strings are considered equal"
如果兩個(gè)字符串或者兩個(gè)字符的擴(kuò)展字形集群正則等價(jià),那么這兩個(gè)字符串或者字符相等。如果兩個(gè)字符串或者兩個(gè)字符有相同的語(yǔ)言意義和外觀,那么他們的擴(kuò)展字形集群相等,即使他們是不同的Unicode標(biāo)量合成的。例如,LATIN SMALL LETTER E WITH ACUTE (U+00E9)
與LATIN SMALL LETTER E (U+0065)
和COMBINING ACUTE ACCENT (U+0301)
的組合是相等的:
// "Voulez-vous un café?" using LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"
// "Voulez-vous un café?" using 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")
}
// Prints "These two strings are considered equal"
相反,英語(yǔ)的LATIN CAPITAL LETTER A
(U+0041
, 或者 "A"
),不等于俄語(yǔ)的CYRILLIC CAPITAL LETTER A
(U+0410
, 或者 "А"
),他們看起來(lái)非常相似,但是語(yǔ)言意義不一樣:
let latinCapitalLetterA: Character = "\u{41}"
let cyrillicCapitalLetterA: Character = "\u{0410}"
if latinCapitalLetterA != cyrillicCapitalLetterA {
print("These two characters are not equivalent.")
}
// Prints "These two characters are not equivalent."
注意:Swift中的字符和字符串對(duì)區(qū)域不敏感。
前綴和后綴相等 (Prefix and Suffix Equality)
使用hasPrefix(_:)
和hasSuffix(_:)
來(lái)判斷一個(gè)字符串是否有特定的前綴或者后綴。
let romeoAndJuliet = [
"Act 1 Scene 1: Verona, A public place",
"Act 1 Scene 2: Capulet's mansion",
"Act 1 Scene 3: A room in Capulet's mansion",
"Act 1 Scene 4: A street outside Capulet's mansion",
"Act 1 Scene 5: The Great Hall in Capulet's mansion",
"Act 2 Scene 1: Outside Capulet's mansion",
"Act 2 Scene 2: Capulet's orchard",
"Act 2 Scene 3: Outside Friar Lawrence's cell",
"Act 2 Scene 4: A street in Verona",
"Act 2 Scene 5: Capulet's mansion",
"Act 2 Scene 6: Friar Lawrence's cell"
]
var act1SceneCount = 0
for scene in romeoAndJuliet {
if scene.hasPrefix("Act 1 ") {
act1SceneCount += 1
}
}
print("There are \(act1SceneCount) scenes in Act 1")
// Prints "There are 5 scenes in Act 1"
var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
if scene.hasSuffix("Capulet's mansion") {
mansionCount += 1
} else if scene.hasSuffix("Friar Lawrence's cell") {
cellCount += 1
}
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
// Prints "6 mansion scenes; 2 cell scenes"
Unicode表示的字符串 (Unicode Representations of Strings)
當(dāng)一個(gè)Unicode字符串被寫(xiě)入文本文件或者其他內(nèi)存時(shí),字符串的Unicode標(biāo)量會(huì)被以一種Unicode定義的編碼形式編碼。每一種形式將字符串編碼到一個(gè)小方塊,也就是代碼單元。這些形式包括:1)UTF-8編碼形式(把一個(gè)字符串編碼為8-bit代碼單元);2)UTF-16編碼形式(把一個(gè)字符串編碼為16-bit代碼單元);3)UTF-32編碼形式(把一個(gè)字符串編碼為32-bit代碼單元)。
訪(fǎng)問(wèn)其中一種符合Unicode標(biāo)準(zhǔn)表示的字符串:
- UTF-8代碼單元集合(使用
utf8
屬性) - UTF-16代碼單元集合(使用
utf16
屬性) - 21-bit Unicode標(biāo)量,也就是字符串的UTF-32編碼形式(使用
unicodeScalars
屬性訪(fǎng)問(wèn))
下面的例子將以不同的展示方式來(lái)展示這兩個(gè)字符或字符串:1)一個(gè)由D
、o
、g
和!!
(DOUBLE EXCLAMATION MARK
,或者Unicode標(biāo)量U+203C
)組成的字符串;2)字符:?? (DOG FACE
,或者Unicode標(biāo)量U+1F436
)。
UTF-8 展示 (UTF-8 Representation)
遍歷utf8
屬性來(lái)訪(fǎng)問(wèn)字符串的UTF-8展示,這個(gè)屬性是String.UTF8View
類(lèi)型,是無(wú)符號(hào)8-bit (UInt8
)值的集合。
let codeUnit in dogString.utf8 {
print("\(codeUnit) ", terminator: "")
}
print("")
// 68 111 103 226 128 188 240 159 144 182
UTF-16 展示 (UTF-16 Representation)
遍歷utf16
屬性來(lái)訪(fǎng)問(wèn)字符串的UTF-16展示,這個(gè)屬性是String.UTF16View
類(lèi)型,是無(wú)符號(hào)8-bit (UInt8
)值的集合。
for codeUnit in dogString.utf16 {
print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 55357 56374 "
Unicode標(biāo)量展示 (Unicode Scalar Representation)
遍歷unicodeScalars
屬性來(lái)訪(fǎng)問(wèn)字符串的Unicode標(biāo)量展示。這個(gè)屬性是UnicodeScalarView
類(lèi)型,是一個(gè)UnicodeScalar
類(lèi)型的值的集合。
每個(gè)UnicodeScalar
有一個(gè)value
屬性,這個(gè)屬性返回標(biāo)量的21-bit值。
for scalar in dogString.unicodeScalars {
print("\(scalar.value) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 128054 "
作為查詢(xún)value
屬性的另外一種方法,每個(gè)UnicodeScalar
的值可以用來(lái)構(gòu)建一個(gè)新的字符串,如字符串的插值:
for scalar in dogString.unicodeScalars {
print("\(scalar)")
}
// D
// o
// g
// ?
// ??
第三部分完。下個(gè)部分:【Swift 3.1】04 - 集合類(lèi)型 (Collection Types)
如果有錯(cuò)誤的地方,歡迎指正!謝謝!