Swift Tour Learn (二) -- Swift 語法(基本運算符、字符串和字符)

本章將會介紹

基本運算符
字符串和字符

基本運算符

運算符是檢查、改變、合并值的特殊符號或短語。例如,加號(+)將兩個數相加(如 let i = 1 + 2)。更復雜的運算例子包括邏輯與運算符 &&(如 if enteredDoorCode && passedRetinaScan)。

Swift 支持大部分標準 C 語言的運算符,且改進許多特性來減少常規編碼錯誤。如:賦值符(=)不返回值,以防止把想要判斷相等運算符(==)的地方寫成賦值符導致的錯誤。算術運算符(+,-,*,/,%等)會檢測并不允許值溢出,以此來避免保存變量時由于變量大于或小于其類型所能承載的范圍時導致的異常結果。當然允許你使用 Swift 的溢出運算符來實現溢出。

Swift 還提供了 C 語言沒有的表達兩數之間的值的區間運算符(a..<b 和 a...b),這方便我們表達一個區間內的數值。

1. 賦值運算符
2. 算術運算符 + - * /
3. 求余運算符(取模運算符) %
4. 一元負號運算符 -
5. 一元正號運算符 +
6. 組合賦值運算符 +=
7. 比較運算符 == != > < >= <= === !==

注意: Swift 也提供恒等(===)和不恒等(!==)這兩個比較符來判斷兩個對象是否引用同一個對象實例。

當元組中的值可以比較時,你也可以使用這些運算符來比較它們的大小。例如,因為 Int 和 String 類型的值可以比較,所以類型為 (Int, String) 的元組也可以被比較。相反,Bool 不能被比較,也意味著存有布爾類型的元組不能被比較。
比較元組大小會按照從左到右、逐值比較的方式,直到發現有兩個值不等時停止。如果所有的值都相等,那么這一對元組我們就稱它們是相等的。例如:

(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"沒有任何影響,因為元組的比較已經被第一個元素決定了。不過,當元組的第一個元素相同時候,第二個元素將會用作比較-第二行和第三行代碼就發生了這樣的比較。

注意:
Swift 標準庫只能比較七個以內元素的元組比較函數。如果你的元組元素超過七個時,你需要自己實現比較運算符。

8. 三目運算符

三目運算符的特殊在于它是有三個操作數的運算符,它的形式是 問題 ? 答案 1 : 答案 2。它簡潔地表達根據 問題成立與否作出二選一的操作。如果 問題 成立,返回 答案 1 的結果;反之返回 答案 2 的結果。

9. 空合運算符(Nil Coalescing Operator) a??b

空合運算符(a ?? b)將對可選類型 a 進行空判斷,如果 a 包含一個值就進行解封,否則就返回一個默認值 b。表達式 a 必須是 Optional 類型。默認值 b 的類型必須要和 a 存儲值的類型保持一致。
空合運算符是對以下代碼的簡短表達方法:

a != nil ? a! : b

上述代碼使用了三目運算符。當可選類型 a 的值不為空時,進行強制解封(a!),訪問 a 中的值;反之返回默認值 b。無疑空合運算符(??)提供了一種更為優雅的方式去封裝條件判斷和解封兩種行為,顯得簡潔以及更具可讀性。

注意: 如果 a 為非空值(non-nil),那么值 b 將不會被計算。這也就是所謂的短路求值。

下邊例子采用空合運算符,實現了在默認顏色名和可選自定義顏色名之間抉擇:

let defaultColorName = "red"
var userDefinedColorName: String?   //默認值為 nil

var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值為空,所以 colorNameToUse 的值為 "red"

userDefinedColorName 變量被定義為一個可選的 String 類型,默認值為 nil。由于 userDefinedColorName 是一個可選類型,我們可以使用空合運算符去判斷其值。在上一個例子中,通過空合運算符為一個名為 colorNameToUse 的變量賦予一個字符串類型初始值。 由于 userDefinedColorName 值為空,因此表達式 userDefinedColorName ?? defaultColorName 返回 defaultColorName 的值,即 red。

另一種情況,分配一個非空值(non-nil)給 userDefinedColorName,再次執行空合運算,運算結果為封包在 userDefaultColorName 中的值,而非默認值。

userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 非空,因此 colorNameToUse 的值為 "green"
10.區間運算符(Range Operators)
  • 閉區間運算符

閉區間運算符(a...b)定義一個包含從 a 到 b(包括 a 和 b)的所有值的區間。a 的值不能超過 b。 ? 閉區間運算符在迭代一個區間的所有值時是非常有用的,如在 for-in 循環中:

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
  • 半開區間運算符

半開區間運算符(a..<b)定義一個從 a 到 b 但不包括 b 的區間。 之所以稱為半開區間,是因為該區間包含第一個值而不包括最后的值。

半開區間的實用性在于當你使用一個從 0 開始的列表(如數組)時,非常方便地從0數到列表的長度。

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

數組有 4 個元素,但 0..<count 只數到3(最后一個元素的下標),因為它是半開區間。

11.邏輯運算符(Logical Operators)

邏輯運算符的操作對象是邏輯布爾值

  • 邏輯非(!a)
  • 邏輯與(a && b)
  • 邏輯或(a || b)
12.使用括號來明確優先級

字符串和字符

字符串是例如"hello, world","albatross"這樣的有序的Character(字符)類型的值的集合。通過String類型來表示。 一個String的內容可以用許多方式讀取,包括作為一個Character值的集合。

Swift 的String和Character類型提供了快速和兼容 Unicode 的方式供你的代碼使用。創建和操作字符串的語法與 C 語言中字符串操作相似,輕量并且易讀。 字符串連接操作只需要簡單地通過+符號將兩個字符串相連即可。與 Swift 中其他值一樣,能否更改字符串的值,取決于其被定義為常量還是變量。你也可以在字符串內插過程中使用字符串插入常量、變量、字面量表達成更長的字符串,這樣可以很容易的創建自定義的字符串值,進行展示、存儲以及打印。

盡管語法簡易,但String類型是一種快速、現代化的字符串實現。 每一個字符串都是由編碼無關的 Unicode 字符組成,并支持訪問字符的多種 Unicode 表示形式(representations)。

注意:
Swift 的String類型與 Foundation NSString類進行了無縫橋接。Foundation 也可以對String進行擴展,暴露在NSString中定義的方法。 這意味著,如果你在String中調用這些NSString的方法,將不用進行轉換。

1.字符串字面量

您可以在您的代碼中包含一段預定義的字符串值作為字符串字面量。字符串字面量是由雙引號 ("") 包裹著的具有固定順序的文本字符集。 字符串字面量可以用于為常量和變量提供初始值:

let someString = "Some string literal value"

注意someString常量通過字符串字面量進行初始化,Swift 會推斷該常量為String類型。

2.初始化空字符串

要創建一個空字符串作為初始值,可以將空的字符串字面量賦值給變量,也可以初始化一個新的String實例:

var emptyString = ""               // 空字符串字面量
var anotherEmptyString = String()  // 初始化方法
// 兩個字符串均為空并等價。

您可以通過檢查其Bool類型的isEmpty屬性來判斷該字符串是否為空:

if emptyString.isEmpty {
    print("Nothing to see here")
}
// 打印輸出:"Nothing to see here"
3.字符串可變性

您可以通過將一個特定字符串分配給一個變量來對其進行修改,或者分配給一個常量來保證其不會被修改:

var variableString = "Horse"
variableString += " and carriage"
// variableString 現在為 "Horse and carriage"

let constantString = "Highlander"
constantString += " and another Highlander"
// 這會報告一個編譯錯誤 (compile-time error) - 常量字符串不可以被修改。
4.字符串是值類型

Swift 的String類型是值類型。 如果您創建了一個新的字符串,那么當其進行常量、變量賦值操作,或在函數/方法中傳遞時,會進行值拷貝。 任何情況下,都會對已有字符串值創建新副本,并對該新副本進行傳遞或賦值操作。 值類型在 結構體和枚舉是值類型 中進行了詳細描述。

Swift 默認字符串拷貝的方式保證了在函數/方法中傳遞的是字符串的值。 很明顯無論該值來自于哪里,都是您獨自擁有的。 您可以確信傳遞的字符串不會被修改,除非你自己去修改它。

在實際編譯時,Swift 編譯器會優化字符串的使用,使實際的復制只發生在絕對必要的情況下,這意味著您將字符串作為值類型的同時可以獲得極高的性能。

5.使用字符

您可通過for-in循環來遍歷字符串中的characters屬性來獲取每一個字符的值:

for character in "Dog!??".characters {
    print(character)
}
// D
// o
// g
// !
// ??

另外,通過標明一個Character類型并用字符字面量進行賦值,可以建立一個獨立的字符常量或變量:

let exclamationMark: Character = "!"

字符串可以通過傳遞一個值類型為Character的數組作為自變量來初始化:

let catCharacters: [Character] = ["C", "a", "t", "!", "??"]
let catString = String(catCharacters)
print(catString)
// 打印輸出:"Cat!??"
6.連接字符串和字符

字符串可以通過加法運算符(+)相加在一起(或稱“連接”)創建一個新的字符串:

let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome 現在等于 "hello there"

您也可以通過加法賦值運算符 (+=) 將一個字符串添加到一個已經存在字符串變量上:

var instruction = "look over"
instruction += string2
// instruction 現在等于 "look over there"

您可以用append()方法將一個字符附加到一個字符串變量的尾部:

let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome 現在等于 "hello there!"
7.字符串插值

字符串插值是一種構建新字符串的方式,可以在其中包含常量、變量、字面量和表達式。 您插入的字符串字面量的每一項都在以反斜線為前綴的圓括號中:

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message 是 "3 times 2.5 is 7.5"

在上面的例子中,multiplier作為(multiplier)被插入到一個字符串常量量中。 當創建字符串執行插值計算時此占位符會被替換為multiplier實際的值。

multiplier的值也作為字符串中后面表達式的一部分。 該表達式計算Double(multiplier) * 2.5的值并將結果 (7.5) 插入到字符串中。 在這個例子中,表達式寫為(Double(multiplier) * 2.5)并包含在字符串字面量中。

注意:
插值字符串中寫在括號中的表達式不能包含非轉義反斜杠 (),并且不能包含回車或換行符。不過,插值字符串可以包含其他字面量。

8.Unicode

Unicode是一個國際標準,用于文本的編碼和表示。 它使您可以用標準格式表示來自任意語言幾乎所有的字符,并能夠對文本文件或網頁這樣的外部資源中的字符進行讀寫操作。 Swift 的String和Character類型是完全兼容 Unicode 標準的。

Swift 的String類型是基于 Unicode 標量 建立的。 Unicode 標量是對應字符或者修飾符的唯一的21位數字,例如U+0061表示小寫的拉丁字母(LATIN SMALL LETTER A)("a"),U+1F425表示小雞表情(FRONT-FACING BABY CHICK) ("??")。

9. 字符串字面量的特殊字符

字符串字面量可以包含以下特殊字符:

  • 轉義字符\0(空字符)\\(反斜線)\t(水平制表符)\n(換行符)\r(回車符)\"(雙引號)\'(單引號)
  • Unicode 標量,寫成\u{n}(u為小寫),其中n為任意一到八位十六進制數且可用的 Unicode 位碼。

下面的代碼為各種特殊字符的使用示例。 wiseWords常量包含了兩個雙引號。 dollarSign、blackHeart和sparklingHeart常量演示了三種不同格式的 Unicode 標量:

let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imageination is more important than knowledge" - Enistein
let dollarSign = "\u{24}"             // $, Unicode 標量 U+0024
let blackHeart = "\u{2665}"           // ?, Unicode 標量 U+2665
let sparklingHeart = "\u{1F496}"      // ??, Unicode 標量 U+1F496
10.計算字符數量

如果想要獲得一個字符串中Character值的數量,可以使用字符串的characters屬性的count屬性:

let unusualMenagerie = "Koala ??, Snail ??, Penguin ??, Dromedary ??"
print("unusualMenagerie has \(unusualMenagerie.characters.count) characters")
// 打印輸出 "unusualMenagerie has 40 characters"

注意:
可擴展的字符群集可以組成一個或者多個 Unicode 標量。這意味著不同的字符以及相同字符的不同表示方式可能需要不同數量的內存空間來存儲。所以 Swift 中的字符在一個字符串中并不一定占用相同的內存空間數量。因此在沒有獲取字符串的可擴展的字符群的范圍時候,就不能計算出字符串的字符數量。如果您正在處理一個長字符串,需要注意characters屬性必須遍歷全部的 Unicode 標量,來確定字符串的字符數量。

另外需要注意的是通過characters屬性返回的字符數量并不總是與包含相同字符的NSString的length屬性相同。NSString的length屬性是利用 UTF-16 表示的十六位代碼單元數字,而不是 Unicode 可擴展的字符群集。

11.字符串索引

每一個String值都有一個關聯的索引(index)類型,String.Index,它對應著字符串中的每一個Character的位置。

前面提到,不同的字符可能會占用不同數量的內存空間,所以要知道Character的確定位置,就必須從String開頭遍歷每一個 Unicode 標量直到結尾。因此,Swift 的字符串不能用整數(integer)做索引。

使用startIndex屬性可以獲取一個String的第一個Character的索引。使用endIndex屬性可以獲取最后一個Character的后一個位置的索引。因此,endIndex屬性不能作為一個字符串的有效下標。如果String是空串,startIndex和endIndex是相等的。

通過調用 String 的 index(before:) 或 index(after:) 方法,可以立即得到前面或后面的一個索引。您還可以通過調用 index(_:offsetBy:) 方法來獲取對應偏移量的索引,這種方式可以避免多次調用 index(before:) 或 index(after:) 方法。

你可以使用下標語法來訪問 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 屬性會創建一個包含全部索引的范圍(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:) 方法在任意一個確認的并遵循 Collection 協議的類型里面,如上文所示是使用在 String 中,您也可以使用在 Array、Dictionary 和 Set中。

12.插入和刪除

調用 insert(_:at:) 方法可以在一個字符串的指定索引插入一個字符,調用 insert(contentsOf:at:) 方法可以在一個字符串的指定索引插入一個段字符串。

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome 變量現在等于 "hello!"

welcome.insert(contentsOf:" there".characters, at: welcome.index(before: welcome.endIndex))
// welcome 變量現在等于 "hello there!"

調用 remove(at:) 方法可以在一個字符串的指定索引刪除一個字符,調用 removeSubrange(_:) 方法可以在一個字符串的指定索引刪除一個子字符串。

welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome 現在等于 "hello there"

let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome 現在等于 "hello"

注意: 您可以使用 insert(_:at:)insert(contentsOf:at:)remove(at:)removeSubrange(_:) 方法在任意一個確認的并遵循 RangeReplaceableCollection 協議的類型里面,如上文所示是使用在 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"
  • 前綴/后綴相等 通過調用字符串的hasPrefix(_:)/hasSuffix(_:)方法來檢查字符串是否擁有特定前綴/后綴,兩個方法均接收一個String類型的參數,并返回一個布爾值。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容