本章將會介紹
基本運算符
字符串和字符
基本運算符
運算符是檢查宗弯、改變脯燃、合并值的特殊符號或短語。例如蒙保,加號(+)將兩個數(shù)相加(如 let i = 1 + 2)辕棚。更復(fù)雜的運算例子包括邏輯與運算符 &&(如 if enteredDoorCode && passedRetinaScan)。
Swift 支持大部分標(biāo)準(zhǔn) C 語言的運算符邓厕,且改進(jìn)許多特性來減少常規(guī)編碼錯誤逝嚎。如:賦值符(=)不返回值,以防止把想要判斷相等運算符(==)的地方寫成賦值符導(dǎo)致的錯誤详恼。算術(shù)運算符(+补君,-,*单雾,/赚哗,%等)會檢測并不允許值溢出,以此來避免保存變量時由于變量大于或小于其類型所能承載的范圍時導(dǎo)致的異常結(jié)果硅堆。當(dāng)然允許你使用 Swift 的溢出運算符來實現(xiàn)溢出屿储。
Swift 還提供了 C 語言沒有的表達(dá)兩數(shù)之間的值的區(qū)間運算符(a..<b 和 a...b),這方便我們表達(dá)一個區(qū)間內(nèi)的數(shù)值渐逃。
1. 賦值運算符
2. 算術(shù)運算符 + - * /
3. 求余運算符(取模運算符) %
4. 一元負(fù)號運算符 -
5. 一元正號運算符 +
6. 組合賦值運算符 +=
7. 比較運算符 == != > < >= <= === !==
注意: Swift 也提供恒等
(===)
和不恒等(!==)
這兩個比較符來判斷兩個對象是否引用同一個對象實例够掠。
當(dāng)元組中的值可以比較時,你也可以使用這些運算符來比較它們的大小茄菊。例如疯潭,因為 Int 和 String 類型的值可以比較,所以類型為 (Int, String) 的元組也可以被比較面殖。相反竖哩,Bool 不能被比較,也意味著存有布爾類型的元組不能被比較脊僚。
比較元組大小會按照從左到右相叁、逐值比較的方式,直到發(fā)現(xiàn)有兩個值不等時停止。如果所有的值都相等增淹,那么這一對元組我們就稱它們是相等的椿访。例如:
(1, "zebra") < (2, "apple") // true,因為 1 小于 2
(3, "apple") < (3, "bird") // true虑润,因為 3 等于 3成玫,但是 apple 小于 bird
(4, "dog") == (4, "dog") // true,因為 4 等于 4拳喻,dog 等于 dog
在上面的例子中哭当,你可以看到,在第一行中從左到右的比較行為舞蔽。因為1小于2荣病,所以(1, "zebra")小于(2, "apple")码撰,不管元組剩下的值如何渗柿。所以"zebra"小于"apple"沒有任何影響,因為元組的比較已經(jīng)被第一個元素決定了脖岛。不過朵栖,當(dāng)元組的第一個元素相同時候,第二個元素將會用作比較-第二行和第三行代碼就發(fā)生了這樣的比較柴梆。
注意:
Swift 標(biāo)準(zhǔn)庫只能比較七個以內(nèi)元素的元組比較函數(shù)陨溅。如果你的元組元素超過七個時,你需要自己實現(xiàn)比較運算符绍在。
8. 三目運算符
三目運算符的特殊在于它是有三個操作數(shù)的運算符门扇,它的形式是 問題 ? 答案 1 : 答案 2。它簡潔地表達(dá)根據(jù) 問題成立與否作出二選一的操作偿渡。如果 問題 成立臼寄,返回 答案 1 的結(jié)果;反之返回 答案 2 的結(jié)果溜宽。
9. 空合運算符(Nil Coalescing Operator) a??b
空合運算符(a ?? b)將對可選類型 a 進(jìn)行空判斷吉拳,如果 a 包含一個值就進(jìn)行解封,否則就返回一個默認(rèn)值 b适揉。表達(dá)式 a 必須是 Optional 類型留攒。默認(rèn)值 b 的類型必須要和 a 存儲值的類型保持一致。
空合運算符是對以下代碼的簡短表達(dá)方法:
a != nil ? a! : b
上述代碼使用了三目運算符嫉嘀。當(dāng)可選類型 a 的值不為空時炼邀,進(jìn)行強制解封(a!),訪問 a 中的值剪侮;反之返回默認(rèn)值 b拭宁。無疑空合運算符(??)提供了一種更為優(yōu)雅的方式去封裝條件判斷和解封兩種行為,顯得簡潔以及更具可讀性。
注意: 如果 a 為非空值(non-nil)红淡,那么值 b 將不會被計算不狮。這也就是所謂的短路求值。
下邊例子采用空合運算符在旱,實現(xiàn)了在默認(rèn)顏色名和可選自定義顏色名之間抉擇:
let defaultColorName = "red"
var userDefinedColorName: String? //默認(rèn)值為 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值為空摇零,所以 colorNameToUse 的值為 "red"
userDefinedColorName 變量被定義為一個可選的 String 類型,默認(rèn)值為 nil桶蝎。由于 userDefinedColorName 是一個可選類型驻仅,我們可以使用空合運算符去判斷其值。在上一個例子中登渣,通過空合運算符為一個名為 colorNameToUse 的變量賦予一個字符串類型初始值噪服。 由于 userDefinedColorName 值為空,因此表達(dá)式 userDefinedColorName ?? defaultColorName 返回 defaultColorName 的值胜茧,即 red粘优。
另一種情況,分配一個非空值(non-nil)給 userDefinedColorName呻顽,再次執(zhí)行空合運算雹顺,運算結(jié)果為封包在 userDefaultColorName 中的值,而非默認(rèn)值廊遍。
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 非空嬉愧,因此 colorNameToUse 的值為 "green"
10.區(qū)間運算符(Range Operators)
- 閉區(qū)間運算符
閉區(qū)間運算符(a...b)定義一個包含從 a 到 b(包括 a 和 b)的所有值的區(qū)間。a 的值不能超過 b喉前。 ? 閉區(qū)間運算符在迭代一個區(qū)間的所有值時是非常有用的没酣,如在 for-in 循環(huán)中:
for index in 1...5 {
print("\(index) * 5 = \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25
- 半開區(qū)間運算符
半開區(qū)間運算符(a..<b)定義一個從 a 到 b 但不包括 b 的區(qū)間。 之所以稱為半開區(qū)間卵迂,是因為該區(qū)間包含第一個值而不包括最后的值裕便。
半開區(qū)間的實用性在于當(dāng)你使用一個從 0 開始的列表(如數(shù)組)時,非常方便地從0數(shù)到列表的長度狭握。
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
print("第 \(i + 1) 個人叫 \(names[i])")
}
// 第 1 個人叫 Anna
// 第 2 個人叫 Alex
// 第 3 個人叫 Brian
// 第 4 個人叫 Jack
數(shù)組有 4 個元素闪金,但 0..<count 只數(shù)到3(最后一個元素的下標(biāo)),因為它是半開區(qū)間论颅。
11.邏輯運算符(Logical Operators)
邏輯運算符的操作對象是邏輯布爾值
- 邏輯非(!a)
- 邏輯與(a && b)
- 邏輯或(a || b)
12.使用括號來明確優(yōu)先級
字符串和字符
字符串是例如"hello, world"哎垦,"albatross"這樣的有序的Character(字符)類型的值的集合。通過String類型來表示恃疯。 一個String的內(nèi)容可以用許多方式讀取漏设,包括作為一個Character值的集合。
Swift 的String和Character類型提供了快速和兼容 Unicode 的方式供你的代碼使用今妄。創(chuàng)建和操作字符串的語法與 C 語言中字符串操作相似郑口,輕量并且易讀鸳碧。 字符串連接操作只需要簡單地通過+符號將兩個字符串相連即可。與 Swift 中其他值一樣犬性,能否更改字符串的值瞻离,取決于其被定義為常量還是變量。你也可以在字符串內(nèi)插過程中使用字符串插入常量乒裆、變量套利、字面量表達(dá)成更長的字符串,這樣可以很容易的創(chuàng)建自定義的字符串值鹤耍,進(jìn)行展示肉迫、存儲以及打印。
盡管語法簡易稿黄,但String類型是一種快速喊衫、現(xiàn)代化的字符串實現(xiàn)。 每一個字符串都是由編碼無關(guān)的 Unicode 字符組成杆怕,并支持訪問字符的多種 Unicode 表示形式(representations)族购。
注意:
Swift 的String類型與 Foundation NSString類進(jìn)行了無縫橋接。Foundation 也可以對String進(jìn)行擴展财著,暴露在NSString中定義的方法联四。 這意味著撑碴,如果你在String中調(diào)用這些NSString的方法撑教,將不用進(jìn)行轉(zhuǎn)換。
1.字符串字面量
您可以在您的代碼中包含一段預(yù)定義的字符串值作為字符串字面量醉拓。字符串字面量是由雙引號 ("") 包裹著的具有固定順序的文本字符集伟姐。 字符串字面量可以用于為常量和變量提供初始值:
let someString = "Some string literal value"
注意someString常量通過字符串字面量進(jìn)行初始化,Swift 會推斷該常量為String類型亿卤。
2.初始化空字符串
要創(chuàng)建一個空字符串作為初始值愤兵,可以將空的字符串字面量賦值給變量,也可以初始化一個新的String實例:
var emptyString = "" // 空字符串字面量
var anotherEmptyString = String() // 初始化方法
// 兩個字符串均為空并等價排吴。
您可以通過檢查其Bool類型的isEmpty屬性來判斷該字符串是否為空:
if emptyString.isEmpty {
print("Nothing to see here")
}
// 打印輸出:"Nothing to see here"
3.字符串可變性
您可以通過將一個特定字符串分配給一個變量來對其進(jìn)行修改秆乳,或者分配給一個常量來保證其不會被修改:
var variableString = "Horse"
variableString += " and carriage"
// variableString 現(xiàn)在為 "Horse and carriage"
let constantString = "Highlander"
constantString += " and another Highlander"
// 這會報告一個編譯錯誤 (compile-time error) - 常量字符串不可以被修改。
4.字符串是值類型
Swift 的String類型是值類型钻哩。 如果您創(chuàng)建了一個新的字符串屹堰,那么當(dāng)其進(jìn)行常量、變量賦值操作街氢,或在函數(shù)/方法中傳遞時扯键,會進(jìn)行值拷貝。 任何情況下珊肃,都會對已有字符串值創(chuàng)建新副本荣刑,并對該新副本進(jìn)行傳遞或賦值操作馅笙。 值類型在 結(jié)構(gòu)體和枚舉是值類型 中進(jìn)行了詳細(xì)描述。
Swift 默認(rèn)字符串拷貝的方式保證了在函數(shù)/方法中傳遞的是字符串的值厉亏。 很明顯無論該值來自于哪里董习,都是您獨自擁有的。 您可以確信傳遞的字符串不會被修改爱只,除非你自己去修改它阱飘。
在實際編譯時,Swift 編譯器會優(yōu)化字符串的使用虱颗,使實際的復(fù)制只發(fā)生在絕對必要的情況下沥匈,這意味著您將字符串作為值類型的同時可以獲得極高的性能。
5.使用字符
您可通過for-in循環(huán)來遍歷字符串中的characters屬性來獲取每一個字符的值:
for character in "Dog!??".characters {
print(character)
}
// D
// o
// g
// !
// ??
另外忘渔,通過標(biāo)明一個Character類型并用字符字面量進(jìn)行賦值高帖,可以建立一個獨立的字符常量或變量:
let exclamationMark: Character = "!"
字符串可以通過傳遞一個值類型為Character的數(shù)組作為自變量來初始化:
let catCharacters: [Character] = ["C", "a", "t", "!", "??"]
let catString = String(catCharacters)
print(catString)
// 打印輸出:"Cat!??"
6.連接字符串和字符
字符串可以通過加法運算符(+)相加在一起(或稱“連接”)創(chuàng)建一個新的字符串:
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome 現(xiàn)在等于 "hello there"
您也可以通過加法賦值運算符 (+=) 將一個字符串添加到一個已經(jīng)存在字符串變量上:
var instruction = "look over"
instruction += string2
// instruction 現(xiàn)在等于 "look over there"
您可以用append()方法將一個字符附加到一個字符串變量的尾部:
let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome 現(xiàn)在等于 "hello there!"
7.字符串插值
字符串插值是一種構(gòu)建新字符串的方式,可以在其中包含常量畦粮、變量散址、字面量和表達(dá)式。 您插入的字符串字面量的每一項都在以反斜線為前綴的圓括號中:
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message 是 "3 times 2.5 is 7.5"
在上面的例子中宣赔,multiplier作為(multiplier)被插入到一個字符串常量量中预麸。 當(dāng)創(chuàng)建字符串執(zhí)行插值計算時此占位符會被替換為multiplier實際的值。
multiplier的值也作為字符串中后面表達(dá)式的一部分儒将。 該表達(dá)式計算Double(multiplier) * 2.5的值并將結(jié)果 (7.5) 插入到字符串中吏祸。 在這個例子中,表達(dá)式寫為(Double(multiplier) * 2.5)并包含在字符串字面量中钩蚊。
注意:
插值字符串中寫在括號中的表達(dá)式不能包含非轉(zhuǎn)義反斜杠 ()贡翘,并且不能包含回車或換行符。不過砰逻,插值字符串可以包含其他字面量鸣驱。
8.Unicode
Unicode是一個國際標(biāo)準(zhǔn),用于文本的編碼和表示蝠咆。 它使您可以用標(biāo)準(zhǔn)格式表示來自任意語言幾乎所有的字符踊东,并能夠?qū)ξ谋疚募蚓W(wǎng)頁這樣的外部資源中的字符進(jìn)行讀寫操作。 Swift 的String和Character類型是完全兼容 Unicode 標(biāo)準(zhǔn)的刚操。
Swift 的String類型是基于 Unicode 標(biāo)量 建立的闸翅。 Unicode 標(biāo)量是對應(yīng)字符或者修飾符的唯一的21位數(shù)字,例如U+0061表示小寫的拉丁字母(LATIN SMALL LETTER A)("a")赡茸,U+1F425表示小雞表情(FRONT-FACING BABY CHICK) ("??")缎脾。
9. 字符串字面量的特殊字符
字符串字面量可以包含以下特殊字符:
- 轉(zhuǎn)義字符
\0(空字符)
、\\(反斜線)
占卧、\t(水平制表符)
遗菠、\n(換行符)
联喘、\r(回車符)
、\"(雙引號)
辙纬、\'(單引號)
豁遭。 - Unicode 標(biāo)量,寫成
\u{n}
(u為小寫)贺拣,其中n為任意一到八位十六進(jìn)制數(shù)且可用的 Unicode 位碼蓖谢。
下面的代碼為各種特殊字符的使用示例。 wiseWords常量包含了兩個雙引號譬涡。 dollarSign闪幽、blackHeart和sparklingHeart常量演示了三種不同格式的 Unicode 標(biāo)量:
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imageination is more important than knowledge" - Enistein
let dollarSign = "\u{24}" // $, Unicode 標(biāo)量 U+0024
let blackHeart = "\u{2665}" // ?, Unicode 標(biāo)量 U+2665
let sparklingHeart = "\u{1F496}" // ??, Unicode 標(biāo)量 U+1F496
10.計算字符數(shù)量
如果想要獲得一個字符串中Character值的數(shù)量,可以使用字符串的characters屬性的count屬性:
let unusualMenagerie = "Koala ??, Snail ??, Penguin ??, Dromedary ??"
print("unusualMenagerie has \(unusualMenagerie.characters.count) characters")
// 打印輸出 "unusualMenagerie has 40 characters"
注意:
可擴展的字符群集可以組成一個或者多個 Unicode 標(biāo)量涡匀。這意味著不同的字符以及相同字符的不同表示方式可能需要不同數(shù)量的內(nèi)存空間來存儲盯腌。所以 Swift 中的字符在一個字符串中并不一定占用相同的內(nèi)存空間數(shù)量。因此在沒有獲取字符串的可擴展的字符群的范圍時候陨瘩,就不能計算出字符串的字符數(shù)量羡铲。如果您正在處理一個長字符串瞳抓,需要注意characters屬性必須遍歷全部的 Unicode 標(biāo)量末患,來確定字符串的字符數(shù)量军洼。
另外需要注意的是通過characters屬性返回的字符數(shù)量并不總是與包含相同字符的NSString的length屬性相同。NSString的length屬性是利用 UTF-16 表示的十六位代碼單元數(shù)字甚淡,而不是 Unicode 可擴展的字符群集大诸。
11.字符串索引
每一個String值都有一個關(guān)聯(lián)的索引(index)類型,String.Index材诽,它對應(yīng)著字符串中的每一個Character的位置底挫。
前面提到,不同的字符可能會占用不同數(shù)量的內(nèi)存空間脸侥,所以要知道Character的確定位置,就必須從String開頭遍歷每一個 Unicode 標(biāo)量直到結(jié)尾盈厘。因此睁枕,Swift 的字符串不能用整數(shù)(integer)做索引。
使用startIndex屬性可以獲取一個String的第一個Character的索引沸手。使用endIndex屬性可以獲取最后一個Character的后一個位置的索引外遇。因此,endIndex屬性不能作為一個字符串的有效下標(biāo)契吉。如果String是空串跳仿,startIndex和endIndex是相等的。
通過調(diào)用 String 的 index(before:) 或 index(after:) 方法捐晶,可以立即得到前面或后面的一個索引菲语。您還可以通過調(diào)用 index(_:offsetBy:) 方法來獲取對應(yīng)偏移量的索引妄辩,這種方式可以避免多次調(diào)用 index(before:) 或 index(after:) 方法。
你可以使用下標(biāo)語法來訪問 String 特定索引的 Character山上。
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)
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 ! "
注意:
您可以使用 startIndex 和 endIndex 屬性或者 index(before:) 佩憾、index(after:) 和 index(_:offsetBy:) 方法在任意一個確認(rèn)的并遵循 Collection 協(xié)議的類型里面哮伟,如上文所示是使用在 String 中,您也可以使用在 Array妄帘、Dictionary 和 Set中楞黄。
12.插入和刪除
調(diào)用 insert(_:at:) 方法可以在一個字符串的指定索引插入一個字符,調(diào)用 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!"
調(diào)用 remove(at:) 方法可以在一個字符串的指定索引刪除一個字符谅辣,調(diào)用 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"
注意: 您可以使用
insert(_:at:)
婶恼、insert(contentsOf:at:)
桑阶、remove(at:)
和removeSubrange(_:)
方法在任意一個確認(rèn)的并遵循RangeReplaceableCollection
協(xié)議的類型里面,如上文所示是使用在 String 中勾邦,您也可以使用在 Array蚣录、Dictionary 和 Set 中。
13.比較字符串
Swift 提供了三種方式來比較文本值:字符串字符相等眷篇、前綴相等和后綴相等萎河。
- 字符串/字符可以用等于操作符(==)和不等于操作符(!=)
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")
}
// 打印輸出 "These two strings are considered equal"
- 前綴/后綴相等 通過調(diào)用字符串的
hasPrefix(_:)/hasSuffix(_:)
方法來檢查字符串是否擁有特定前綴/后綴,兩個方法均接收一個String類型的參數(shù)蕉饼,并返回一個布爾值虐杯。