Swift5.2-字符串和字符(中文文檔)

引言

繼續學習Swift文檔,從上一章節:基本的操做,咱們學習了Swift上基本的一些運算符操做,基本上跟C、OC上的運算符同樣;不過,在Swift上有一些特有的運算符,能夠簡化代碼,如??操做符、...操做符、..<操做符和單邊值域,不熟悉的朋友能夠到上一章節去看這部份內容(在七、八、9三個小節)。如今,咱們學習Swift的字符串和字符相關的內容。因爲篇幅較長,這裏分篇來記錄,接下來,開始吧!html

這一章節內容比較簡單,已經熟悉的朋友能夠跳過,進入下一章節:集合類型web

字符串和字符

字符串是一系列字符,如「hello, world」或「albatross」。Swift字符串由字符串類型表示。字符串的內容能夠經過各類方式訪問,包括做爲字符值的集合訪問。swift

Swift的字符串和字符類型提供了一種快速、兼容unicode的方式來處理代碼中的文本。字符串的語法建立和操縱輕便,可讀性強,與字符串的語法相似於c字符串鏈接很是簡單,只需將兩個字符串+操做符,和字符串可變性由常量或變量之間選擇,就像任何其餘價值迅速。還可使用字符串將常量、變量、文本和表達式插入到更長的字符串中,這個過程稱爲字符串插值。這使得爲顯示、存儲和打印建立自定義字符串值變得很容易。api

儘管語法簡單,Swift的字符串類型是一種快速、現代的字符串實現。每一個字符串都由與編碼無關的Unicode字符組成,並支持以各類Unicode表示形式訪問這些字符。數組

Swift的字符串類型經過Foundation的NSString類進行鏈接。Foundation還擴展了String以公開NSString定義的方法。這意味着,若是你導入Foundation,你能夠在字符串上訪問那些NSString方法而不須要強制轉換。性能優化

有關在Foundation和Cocoa中使用String的更多信息,請參見String和NSString之間架橋bash

1 字符串

能夠在代碼中以字符串文本的形式包含預約義的字符串值。字符串文字是由雙引號(")包圍的字符序列。app

使用字符串文字做爲常量或變量的初始值:ide

let someString = "Some string literal value"
複製代碼

注意,Swift爲someString常量推斷出一種字符串類型,由於它是用字符串文字值初始化的。函數

1.1 多行字符串

若是你須要一個跨越幾行的字符串,使用一個多行字符串文本-由三個雙引號包圍的字符序列:

let quotation = """ The White Rabbit put on his spectacles. "Where shall I begin,
please your Majesty?" he asked. "Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop." """
複製代碼

多行字符串文字包括它的開始和結束引號之間的全部行。字符串從第一行開始在開始引號(「」」)以後,結束在結束引號以前,這意味着下面的字符串都不是以換行符開始或結束的:

let singleLineString = "These are the same."
let multilineString = """ These are the same. """
複製代碼

當源代碼在多行字符串文字中包含換行符時,該換行符也會出如今字符串的值中。若是你想使用換行符使你的源代碼更容易閱讀,但你不想換行符成爲字符串值的一部分,在這些行的末尾寫一個反斜槓():

let softWrappedQuotation = """ The White Rabbit put on his spectacles. "Where shall I begin, \
please your Majesty?" he asked. "Begin at the beginning," the King said gravely, "and go on \
till you come to the end; then stop." """
複製代碼

要使多行字符串以換行符開始或結束,請編寫一個空白行做爲第一行或最後一行。例如:

let lineBreaks = """ This string starts with a line break. It also ends with a line break. """
複製代碼

能夠縮進多行字符串以匹配周圍的代碼。結束引號(""")以前的空白告訴Swift在全部其餘行以前忽略哪些空白。可是,若是除了在結束引號以前還在一行的開始處添加空格,那麼該空格就包含在內。

image.png

在上面的示例中,即便整個多行字符串是縮進的,字符串的第一行和最後一行並不以任何空格開始。中間的一行比結束引號有更多的縮進,因此它以額外的四空格縮進開始。

1.2 字符串中的特殊字符

字符串中可包含如下特殊的字符:

  • 轉義特殊字符\0(空字符)、\(反斜槓)、\t(水平製表符)、\n(換行)、\r(回車)、"(雙引號)和'(單引號)
  • 任意的Unicode標量值,寫爲\u{n},其中n是1-8位的十六進制數字(Unicode將在下面的Unicode中討論)

下面的代碼顯示了這些特殊字符的四個示例。wiseWords常量包含兩個轉義雙引號。dollarSign、blackHeart和sparklingHeart常量演示了Unicode標量格式:

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
複製代碼

由於多行字符串字面量使用三個雙引號而不是一個,因此能夠在多行字符串字面量中包含一個雙引號(")而不用轉義。要在多行字符串中包含文本""",必須轉義至少一個引號。例如:

let threeDoubleQuotationMarks = """ Escaping the first quotation mark \""" Escaping all three quotation marks \"\"\" """
複製代碼

1.3 長字符串分隔符

能夠在擴展分隔符中放置字符串文字,以便在字符串中包含特殊字符而不調用其效果。您將字符串放在引號(")中,並用數字符號(#)包圍它。例如,打印字符串文本#" line1 \nLine 2"#將打印換行轉義序列(\n),而不是跨兩行打印字符串。

若是須要字符串文本中的字符的特殊效果,則匹配轉義字符()後面的字符串中的數字符號數量。例如,若是您的字符串是#"Line 1\nLine 2"#,而且您但願換行,您可使用#"Line 1#nLine 2"#來代替。相似地,###"Line1###nLine2"###也會中斷該行。

使用擴展分隔符建立的字符串文本也能夠是多行字符串文本。可使用擴展的分隔符將文本"""包含在多行字符串中,覆蓋結束文本的默認行爲。例如:

let threeMoreDoubleQuotationMarks = #"""
Here are three more double quotes: """ """#
複製代碼

2 初始化空字符串

要建立一個空字符串值做爲構建更長的字符串的起點,能夠爲變量分配一個空字符串文字,或者使用初始化器語法初始化一個新的字符串實例:

var emptyString = ""               // empty string literal
var anotherEmptyString = String()  // initializer syntax
// these two strings are both empty, and are equivalent to each other
複製代碼

經過檢查一個字符串的布爾值isEmpty屬性來判斷它是否爲空:

if emptyString.isEmpty {
    print("Nothing to see here")
}
// Prints "Nothing to see here"
複製代碼

3 字符串可變性

你能夠經過將一個特定的字符串賦值給一個變量(在這種狀況下它能夠被修改),或者賦值給一個常量(在這種狀況下它不能被修改)來指示它是否能夠被修改(或改變):

var variableString = "Horse"
variableString += " and carriage"
// variableString is now "Horse and carriage"

let constantString = "Highlander"
constantString += " and another Highlander"
// this reports a compile-time error - a constant string cannot be modified
複製代碼

注意 這種方法不一樣於Objective-C和Cocoa中的字符串改變,在那裏你能夠在兩個類(NSString和NSMutableString)之間進行選擇,以指示字符串是否能夠被改變。

4 字符串是值類型

Swift的字符串類型是一種值類型。若是您建立了一個新的字符串值,當它被傳遞給一個函數或方法時,或者當它被分配給一個常量或變量時,該字符串值會被複制。在每種狀況下,都會建立現有字符串值的新副本,並傳遞或分配新副本,而不是原始值。值類型在結構體和枚舉是值類型中有描述。

Swift的copy-by-default字符串行爲確保了當一個函數或方法傳遞給你一個字符串值時,無論它來自哪裏,你都清楚地擁有這個字符串值。您能夠確信,您傳遞的字符串不會被修改,除非您本身修改它。

在幕後,Swift的編譯器優化了字符串的使用,這樣實際的複製只在絕對必要的時候纔會發生。這意味着當您使用字符串做爲值類型時,老是可以得到良好的性能。

5 使用字符

你能夠經過for-in循環遍歷字符串來訪問單個字符的值:

for character in "Dog!🐶" {
    print(character)
}
// D
// o
// g
// !
// 🐶
複製代碼

for-in循環參見For-In Loops

或者,您能夠經過提供字符類型註釋,從單字符字符串文本建立獨立的字符常量或變量:

let exclamationMark: Character = "!"
複製代碼

字符串值能夠經過將一個字符值數組做爲參數傳遞給它的初始化器來構造:

let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
let catString = String(catCharacters)
print(catString)
// Prints "Cat!🐱"
複製代碼

6 鏈接字符串和字符

可使用加法運算符(+)將字符串值相加(或鏈接),從而建立一個新的字符串值:

let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome now equals "hello there"
複製代碼

您還可使用添加賦值操做符(+=)將字符串值追加到現有字符串變量:

var instruction = "look over"
instruction += string2
// instruction now equals "look over there"
複製代碼

你可使用字符串類型的append()方法將一個字符值附加到一個字符串變量:

let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome now equals "hello there!"
複製代碼

注意 不能將字符串或字符附加到現有的字符變量,由於字符值必須只包含單個字符。

若是使用多行字符串文原本構建較長的字符串的行,則但願字符串中的每一行都以換行符結束,包括最後一行。例如:

let badStart = """ one two """
let end = """ three """
print(badStart + end)
// Prints two lines:
// one
// twothree

let goodStart = """ one two """
print(goodStart + end)
// Prints three lines:
// one
// two
// three
複製代碼

在上面的代碼中,將badStart與end鏈接會產生一個兩行字符串,這不是預期的結果。由於badStart的最後一行沒有以換行符結束,因此該行與end的第一行合併在一塊兒。相反,goodStart的兩行末尾都有一個換行符,因此當它與end合併時,結果如預期的同樣有三行。

7 字符串插值

字符串插值是一種經過將常量、變量、文本和表達式的值包含在字符串文本中構造新字符串值的方法。能夠在單行和多行字符串文本中使用字符串插值。你插入到字符串文字中的每一項都被包裹在一對括號中,加上一個反斜槓():

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"
複製代碼

在上面的示例中,將multiplier的值做爲(multiplier)插入字符串文字中。當計算字符串插值以建立實際字符串時,將用乘數的實際值替換此佔位符。

multiplier的值也是字符串後面較大表達式的一部分。這個表達式計算Double(乘法器)* 2.5的值,並將結果(7.5)插入到字符串中。在本例中,當表達式包含在字符串文字中時,它被寫成(Double(multiplier) * 2.5)。

可使用擴展字符串分隔符建立包含字符的字符串,不然這些字符將被視爲字符串插值。例如:

print(#"Write an interpolated string in Swift using \(multiplier)."#)
// Prints "Write an interpolated string in Swift using \(multiplier)."
複製代碼

若要在使用擴展分隔符的字符串中使用字符串插值,請將反斜槓後的數字符號數與字符串開始和結束的數字符號數匹配。例如:

print(#"6 times 7 is \#(6 * 7)."#)
// Prints "6 times 7 is 42."
複製代碼

注意 在內插字符串的括號內編寫的表達式不能包含未轉義的反斜槓()、回車符或換行符。可是,它們能夠包含其餘字符串文字。

8 Unicode

Unicode是一種國際標準,用於在不一樣的書寫系統中編碼、表示和處理文本。它使您可以以標準化的形式表示來自任何語言的幾乎全部字符,而且可以從外部源(如文本文件或web頁面)讀寫這些字符。Swift的字符串和字符類型徹底兼容unicode,如本節所述。

8.1 Unicode標量值

在後臺,Swift的本地字符串類型是由Unicode標量值構建的。Unicode標量值是一個獨特的21-bit數字字符或修飾符,如拉丁字母小寫字母 A(「a」):U+0061 ,或前置小雞(「🐥」):U+1F425。

注意,並非全部的21位Unicode標量值都被分配給字符—有些標量被保留以供未來分配或在UTF-16編碼中使用。分配給字符的標量值一般也有一個名稱,例如上面示例中的拉丁小寫字母a和前面的CHICK。

8.2 擴展字形集羣

Swift字符類型的每一個實例都表明一個擴展的字母簇。擴展的grapheme集羣是由一個或多個Unicode標量組成的序列,(在組合時)生成一個可讀的字符。

這是一個例子。字母e能夠表示爲單個Unicode標量é(帶銳角的拉丁小寫字母e,或U+00E9)。可是,同一個字母也能夠表示爲一對標量—標準字母e(拉丁小寫字母e,或U+0065),後面是合併的重音標量(U+0301)。組合的重音標量以圖形方式應用於它前面的標量,在支持unicode的文本呈現系統呈現時將e轉換爲é。

在這兩種狀況下,字母é都表示爲一個Swift字符值,該值表示一個擴展的字母羣。在第一種狀況下,集羣包含單個標量;在第二種狀況下,它是兩個標量的一個簇:

let eAcute: Character = "\u{E9}"                         // é
let combinedEAcute: Character = "\u{65}\u{301}"          // e followed by ́
// eAcute is é, combinedEAcute is é
複製代碼

擴展的grapheme集羣是一種靈活的方式,能夠將許多複雜的腳本字符表示爲單個字符值。例如,韓語字母中的韓語音節能夠表示爲預先組成或分解的序列。這兩種表示形式在Swift中均可以做爲單個字符值:

let precomposed: Character = "\u{D55C}"                  // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"   // ᄒ, ᅡ, ᆫ
// precomposed is 한, decomposed is 한
複製代碼

擴展的grapheme集羣容許使用標量來封裝標記(例如組合封裝圓,或U+20DD),從而將其餘Unicode標量封裝爲單個字符值的一部分:

let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute is é⃝
複製代碼

Unicode區域指示符號的標量能夠成對組合成一個字符值,例如區域指示符號字母U (U+1F1FA)和區域指示符號字母S (U+1F1F8)的組合:

let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS is 🇺🇸
複製代碼

9 計算字符

要檢索字符串中字符值的計數,請使用字符串的count屬性:

let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
print("unusualMenagerie has \(unusualMenagerie.count) characters")
// Prints "unusualMenagerie has 40 characters"
複製代碼

注意,Swift對字符值使用擴展的grapheme集羣意味着字符串鏈接和修改可能並不老是影響字符串的字符數。

舉個例子,若是你初始化一個新的字符串與四字咖啡館,而後添加一個結合急性口音(U + 0301)結束的字符串,由此產生的字符串仍然有字符計數4 é第四個字符,不是e:

var word = "cafe"
print("the number of characters in \(word) is \(word.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.count)")
// Prints "the number of characters in café is 4"

複製代碼

注意 擴展的grapheme集羣能夠由多個Unicode標量組成。這意味着不一樣的字符——以及相同字符的不一樣表示形式——須要存儲不一樣數量的內存。正由於如此,Swift中的字符在字符串表示中佔用的內存並不相同。所以,若是不遍歷字符串以肯定擴展的grapheme集羣邊界,就沒法計算字符串中的字符數。若是使用特別長的字符串值,請注意count屬性必須遍歷整個字符串中的Unicode標量,以肯定該字符串的字符。

count屬性返回的字符數並不老是與包含相同字符的NSString的length屬性相同。NSString的長度是基於字符串UTF-16表示中的16位代碼單元的數量,而不是字符串中的Unicode擴展字符羣的數量。

10 訪問和修改字符串

能夠經過字符串的方法和屬性或下標語法訪問和修改字符串。

10.1 字符串索引

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

如上所述,不一樣的字符須要存儲不一樣的內存,所以,爲了肯定哪一個字符位於特定位置,必須從該字符串的開始或結束遍歷每一個Unicode標量。因爲這個緣由,Swift字符串不能被整數值索引。

使用startIndex屬性訪問字符串第一個字符的位置。endIndex屬性是字符串中最後一個字符以後的位置。所以,endIndex屬性不是字符串下標的有效參數。若是字符串爲空,startIndex和endIndex相等。 (注:注意標紅的地方)

使用String的index(before:)和index(after:)方法訪問給定索引以前和以後的索引。要訪問離給定索引更遠的索引,可使用index(_:offsetBy:)方法,而不是屢次調用其中一個方法。

可使用下標語法訪問特定字符串索引處的字符。

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
複製代碼

試圖訪問字符串範圍以外的索引或字符串範圍以外的索引中的字符將觸發運行時錯誤。

greeting[greeting.endIndex] // Error
greeting.index(after: greeting.endIndex) // Error
複製代碼

使用indices屬性來訪問字符串中單個字符的全部索引。

for index in greeting.indices {
    print("\(greeting[index]) ", terminator: "")
}
// Prints "G u t e n T a g ! "
複製代碼

注意 你能夠在任何符合集合協議的類型上使用startIndex和endIndex屬性以及index(before:), index(after:)和index(_:offsetBy:)方法。這包括字符串(如這裏所示)以及數組、字典和Set等集合類型。

10.2 插入和刪除

使用insert(_:at:)方法在指定索引處插入一個字符,使用insert(contentsOf:at:)方法在指定索引處插入另外一個字符串的內容。

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome now equals "hello!"

welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there!"
複製代碼

使用remove(at:)方法從字符串中刪除指定索引處的單個字符;使用removeSubrange(_:)方法刪除指定範圍內的子字符串:

welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there"

let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome now equals "hello"
複製代碼

注意 你能夠在任何符合RangeReplaceableCollection協議的類型上使用insert(:at:)、insert(contentsOf:at:)、remove(at:)和removeSubrange(:)方法。這包括字符串(如這裏所示)以及數組、字典和Set等集合類型。

10.3 子字符串

當您從字符串中獲取子字符串時——例如,使用下標或相似prefix(_:)的方法——結果是substring的實例,而不是其餘字符串。Swift中的子字符串具備與字符串相同的大部分方法,這意味着您能夠像處理字符串同樣處理子字符串。可是,與字符串不一樣的是,在對字符串執行操做時,子字符串的使用時間很短。當準備長時間存儲結果時,能夠將子字符串轉換爲String的實例。例如:

let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning is "Hello"

// Convert the result to a String for long-term storage.
let newString = String(beginning)
複製代碼

與字符串同樣,每一個子字符串都有一個存儲組成子字符串的字符的內存區域。字符串和子字符串之間的區別在於,做爲性能優化,子字符串能夠重用用於存儲原始字符串的部份內存,或用於存儲另外一個子字符串的部份內存。(字符串有相似的優化,但若是兩個字符串共享內存,它們是相等的。)這種性能優化意味着,在修改字符串或子字符串以前,沒必要爲複製內存付出性能成本。如上所述,子字符串不適合長期存儲—由於它們重用原始字符串的存儲,因此只要使用它的任何子字符串,就必須將整個原始字符串保存在內存中。

在上面的例子中,greeting是一個字符串,這意味着它有一個存儲組成字符串的字符的內存區域。由於「開始」是「問候語」的一個子串,它重複了問候語所使用的記憶。相反,newString是一個字符串——當它從子字符串建立時,它有本身的存儲空間。下圖顯示了這些關係:

image.png

注意 字符串和子字符串都符合StringProtocol協議,這意味着字符串操做函數接受StringProtocol值一般很方便。可使用字符串或子字符串值調用此類函數。

11 比較字符串

Swift提供了三種比較文本值的方法:字符串和字符相等、前綴相等和後綴相等。

11.1 字符串和字符相等

字符串和字符是否相等用「equal to」操做符(==)和「not equal to」操做符(!=)檢查,如比較操做符中所述:

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"
複製代碼

若是兩個字符串值(或兩個字符值)的擴展grapheme集羣在規範上是等效的,則認爲它們是相等的。若是擴展的grapheme集羣具備相同的語言意義和外觀,那麼它們在標準上是等價的,即便它們是由不一樣的Unicode標量在幕後組成的。

例如,拉丁字母E加上銳音(U+00E9)在標準上等同於拉丁字母E (U+0065)後面加上銳音(U+0301)。這兩種擴展的grapheme集羣都是表示字符é的有效方式,所以它們在規範上被認爲是等價的:

// "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"
複製代碼

相反,拉丁大寫字母A(U + 0041,或「A」),是用於英語,並不等同於斯拉夫字母大寫字母A(U + 0410,或「А」),是俄語。這兩個角色在視覺上很類似,但語言意義不一樣:

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中的字符串和字符比較對語言環境不敏感。

11.2 前綴和後綴相等

要檢查一個字符串是否有特定的字符串前綴或後綴,能夠調用該字符串的hasPrefix(:)和hasSuffix(:)方法,這兩個方法都有一個string類型的參數並返回一個布爾值。

下面的例子考慮了一個字符串數組,它表明了莎士比亞的《羅密歐與朱麗葉》的前兩幕中的場景位置:

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"
]
複製代碼

你可使用hasPrefix(_:)方法和romeoAndJuliet數組來計算第一幕的場景數:

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"
複製代碼

一樣,使用hasSuffix(_:)方法來計算髮生在凱普萊特大廈和勞倫斯修士的牢房內或周圍的場景的數量:

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"
複製代碼

注意 hasPrefix(:)和hasSuffix(:)方法在每一個字符串中的擴展grapheme集羣之間逐字符執行標準等價性比較,如字符串和字符相等性中所述。

12 字符串的Unicode表示

當將Unicode字符串寫入文本文件或其餘存儲中時,該字符串中的Unicode標量將以幾種Unicode定義的編碼形式之一進行編碼。每一個表單都將字符串編碼爲小塊,稱爲代碼單元。其中包括UTF-8編碼形式(將字符串編碼爲8位代碼單元)、UTF-16編碼形式(將字符串編碼爲16位代碼單元)和UTF-32編碼形式(將字符串編碼爲32位代碼單元)。

Swift提供了幾種不一樣的方式來訪問字符串的Unicode表示。您可使用for-in語句遍歷該字符串,以訪問做爲Unicode擴展字符羣的單個字符值。這個過程在字符處理中有描述。

或者,訪問其餘三種符合unicode的表示形式之一的字符串值:

  • UTF-8代碼單元的集合(經過字符串的utf8屬性訪問)
  • UTF-16代碼單元的集合(經過字符串的utf16屬性訪問)
  • 一個由21位Unicode標量值組成的集合,至關於字符串的UTF-32編碼形式(經過字符串的unicodeScalars屬性訪問)

下面每個例子展現了一個不一樣的表示下面的字符串,這是由人物的D, o, g,‼(雙感嘆號,或者Unicode標量U+203C),和🐶字符(狗臉,或者Unicode標量U+1F436):

let dogString = "Dog‼🐶"
複製代碼

12.1 UTF-8 表示

經過迭代字符串的utf8屬性,能夠訪問字符串的UTF-8表示形式。此屬性的類型爲String。UTF8View,它是一個無符號8位(UInt8)值的集合,對應字符串UTF-8表示的每一個字節:

image.png

for codeUnit in dogString.utf8 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 226 128 188 240 159 144 182 "
複製代碼

在上面的示例中,前三個十進制codeUnit值(6八、1十一、103)表示字符D、o和g,它們的UTF-8表示與它們的ASCII表示相同。接下來的三個十進制代碼單元值(22六、12八、188)是雙感嘆號字符的三字節UTF-8表示形式。後四個codeUnit值(240、15九、14四、182)是狗臉字符的四字節UTF-8表示。

12.2 UTF-16表示

經過迭代字符串的utf16屬性,能夠訪問字符串的UTF-16表示。此屬性的類型爲String。UTF16View是一個無符號16位(UInt16)值的集合,每一個值對應字符串UTF-16表示中的16位代碼單元:

image.png

for codeUnit in dogString.utf16 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 55357 56374 "
複製代碼

一樣,前三個codeUnit值(6八、1十一、103)表示字符D、o和g,它們的UTF-16代碼單位與字符串的UTF-8表示中的值相同(由於這些Unicode標量表示ASCII字符)。

第四個codeUnit值(8252)是十六進制值203C的十進制等價物,203C表示雙感嘆號字符的Unicode標量U+203C。這個字符能夠用UTF-16表示爲單個代碼單元。

第五個和第六個codeUnit值(55357和56374)是狗臉字符的UTF-16代理項對錶示。這些值分別是U+D83D的高代理值(十進制值55357)和U+DC36的低代理值(十進制值56374)。

12.3 Unicode標量表示

經過迭代字符串的unicodeScalars屬性,能夠訪問字符串值的Unicode標量表示形式。此屬性的類型是UnicodeScalarView,它是UnicodeScalar類型的值的集合。

每一個UnicodeScalar都有一個value屬性,它返回標量的21位值,用UInt32值表示:

image.png

for scalar in dogString.unicodeScalars {
    print("\(scalar.value) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 128054 "
複製代碼

前三個UnicodeScalar值(6八、1十一、103)的值屬性再次表示字符D、o和g。

第四個codeUnit值(8252)也是十六進制值203C的十進制等價物,203C表示Unicode標量U+203C,表示雙感嘆號字符。

第五個也是最後一個UnicodeScalar的值屬性128054是十六進制值1F436的十進制等價值,它表示用於DOG FACE字符的Unicode標量U+1F436。

除了查詢它們的值屬性,每一個UnicodeScalar值也能夠用來構造一個新的字符串值,例如字符串插值:

for scalar in dogString.unicodeScalars {
    print("\(scalar) ")
}
// D
// o
// g
// ‼
// 🐶
複製代碼

總結

這一章節講的主要是字符串和字符相關的內容,包括字符串的初始化,多行字符串如何表示,字符串的可變性,字符串是值類型,如何使用字符,鏈接字符串和字符的方法,字符串差值的用法,Unicode,以及字符串的屬性的用法。Swift上字符串的索引使用和OC上的仍是有很大差異的,在文章裏也有相應的案例,你們最好本身敲代碼操做一下,這樣才能加深印象。好了,這一塊內容也比較簡單,已經掌握的朋友能夠跳過去看下一章節。

最後,喜歡這篇文章的朋友能夠給個star,以鼓勵我更加積極地去作這件事,嘿嘿~

上一章節:基本的操做

下一章節:集合類型

參考文檔:Swift - Strings and Characters

相關文章
相關標籤/搜索