Swift 字符串和字符

字符串是一系列字符兆沙,例如"“你好欧芽,世界"hello, world"或"albatross"。Swift字符串由String類型表示葛圃∏樱可以通過各種方式訪問字符串的內(nèi)容憎妙,包括作為字符值的集合。

Swift的String和Character類型提供了一種快速曲楚、兼容Unicode的方法來處理代碼中的文本厘唾。字符串創(chuàng)建和操作的語法,字符串文字語法類似于C龙誊,是輕量級和易讀性的抚垃。字符串連接就像用+運算符組合兩個字符串一樣簡單,字符串可變性是通過在常量或變量之間選擇來管理的载迄,就像Swift中的任何其他值一樣。還可以使用字符串將常量抡蛙、變量护昧、文本和表達式插入較長的字符串中醉蚁,這一過程稱為字符串插值署拟。這使得為顯示稻励、存儲和打印來創(chuàng)建自定義字符串值變得容易魄缚。

盡管語法如此簡單粥鞋,但Swift的String類型是一種快速础锐、現(xiàn)代的字符串實現(xiàn)瞭郑。每個字符串都由編碼獨立的Unicode字符組成胳徽,并支持在各種Unicode表示中訪問這些字符婿屹。

注意
Swift的String類型與Foundation中的NSString類相關聯(lián)灭美。Foundation還擴展了String以公開NSString定義的方法。這意味著昂利,如果導入Foundation届腐,則可以訪問字符串上的那些NSString方法,而無需強制轉(zhuǎn)換蜂奸。
有關在Foundation和Cocoa中使用String的更多信息犁苏,請參閱String和NSString之間的橋接

字符串文字

可以將預定義的字符串值作為字符串文字包含在代碼中扩所。字符串文字是由雙引號(")包圍的字符序列围详。
使用字符串文字作為常量或變量的初始值:

let someString = "Some string literal value"

請注意,Swift會為someString常量推斷一種String類型祖屏,因為它是用字符串文本值初始化的助赞。

多行字符串文字

如果需要跨多行的字符串,請使用多行字符串文字—由三個雙引號包圍的字符序列:

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."
"""

一個多行字符串文字包括它的開始引號和結(jié)束引號之間的所有行袁勺。字符串開始于左引號(""")后的第一行嫉拐,結(jié)束于右引號前的一行,這意味著下面的字符串都不以換行符開頭或結(jié)尾:

let singleLineString = "These are the same."
let multilineString = """
These are the same.
"""

當源代碼在多行字符串文字中包含換行符時魁兼,該換行符也會出現(xiàn)在字符串的值中婉徘。如果要使用換行符使源代碼更易于閱讀漠嵌,但又不希望換行符成為字符串值的一部分,請在這些行的末尾寫一個反斜杠(\):

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."
"""

要生成以換行符開頭或結(jié)尾的多行字符串文字盖呼,請將空行作為第一行或最后一行儒鹿。例如:

let lineBreaks = """

This string starts with a line break.
It also ends with a line break.

"""

多行字符串可以縮進以匹配周圍的代碼。右引號 (""")前的空格告訴Swift几晤,在所有其他行之前忽略哪些空格约炎。但是,如果我們在一行的開頭加上右引號之前的空格蟹瘾,則會包含該空格圾浅。


截屏2021-02-03 下午5.14.47.png

在上面的示例中,即使整個多行字符串文本縮進憾朴,字符串中的第一行和最后一行也不會以任何空格開頭狸捕。中間的行比右引號有更多空格的縮進,所以它從額外的四個空格縮進開始众雷。

字符串文字中的特殊字符

字符串文字可以包括以下特殊字符:

  • 轉(zhuǎn)義的特殊字符\0(空字符)灸拍、\\(反斜杠)、\t(水平制表符)砾省、\n(換行符)鸡岗、\r(回車符)、"(雙引號)和'(單引號)
  • 一個任意的Unicode標量值编兄,寫為\u{n}轩性,其中n是一個1-8位的十六進制數(shù)(Unicode在下面的Unicode中討論)

下面的代碼顯示了這些特殊字符中的四個示例。wiseWords常量包含兩個轉(zhuǎn)義雙引號狠鸳。dollarSign炮姨、blackHeart和SparkingHeart常量演示了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

由于多行字符串文字使用三個雙引號而不是一個雙引號,因此可以在多行字符串文字中包含雙引號(")碰煌,而不必轉(zhuǎn)義它舒岸。若要在多行字符串中包含文本""",請至少轉(zhuǎn)義一個引號芦圾。例如:

let threeDoubleQuotationMarks = """
Escaping the first quotation mark \"""
Escaping all three quotation marks \"\"\"
"""

擴展字符串分隔符

可以在擴展分隔符中放置字符串文字蛾派,以便在字符串中包含特殊字符,而不調(diào)用它們的效果个少。將字符串放在引號(“)內(nèi)洪乍,并用數(shù)字符號(#)圍繞。例如夜焦,打印字符串文字#"Line 1\nLine 2"#打印換行轉(zhuǎn)義序列(\n)壳澳,而不是跨兩行打印字符串。

如果需要字符串文字中字符的特殊效果茫经,請匹配轉(zhuǎn)義符(\)后面字符串中的數(shù)字符號數(shù)巷波。例如萎津,如果字符串是#"Line 1\nLine 2"#,并且要換行抹镊,則可以改用#"Line 1\#nLine 2"#锉屈。類似地,###"Line1\###nLine2"###也會換行垮耳。

使用擴展分隔符創(chuàng)建的字符串文字也可以是多行字符串文字颈渊。可以使用擴展分隔符將文本"""包含在多行字符串中终佛,從而覆蓋以文本結(jié)尾的默認行為俊嗽。例如:

let threeMoreDoubleQuotationMarks = #"""
Here are three more double quotes: """
"""#

初始化空字符串

創(chuàng)建空字符串值來作為生成較長字符串的起點,請將空字符串文字賦給變量铃彰,或使用初始化方法初始化新字符串實例:

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

通過檢查字符串的Boolean isEmpty屬性來確定字符串值是否為空:

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

字符串易變性

我們可以通過將特定字符串賦給變量(在這種情況下可以修改)或常量(在這種情況下不能修改)來表示是否可以修改(或改變)一個特定的字符串:

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)之間進行選擇,以指示字符串是否可以變異豌研。

字符串是值類型

Swift的字符串類型是值類型妹田。如果我們創(chuàng)建了一個新的字符串值唬党,當它被傳遞給函數(shù)或方法時鹃共,或者當它被賦給常量或變量時,該字符串值就會被復制驶拱。在每種情況下霜浴,都會創(chuàng)建現(xiàn)有字符串值的新副本,并傳遞或分配新副本蓝纲,而不是原始版本阴孟。值類型在結(jié)構(gòu)和枚舉是值類型中描述。
Swift的這種默認復制字符串行為税迷,可以確保當一個函數(shù)或方法傳遞給我們一個字符串值時永丝,不管它來自何處,我們都可以明確地擁有該字符串值箭养。我們可以確信慕嚷,傳遞給我們的字符串不會被修改,除非我們自己修改它毕泌。
Swift的編譯器在背后優(yōu)化了字符串的使用喝检,使得真正的復制只在絕對必要的時候發(fā)生。這意味著我們在將字符串作為值類型使用時總是可以獲得很好的性能撼泛。

使用字符

通過使用for-in循環(huán)對字符串進行迭代挠说,可以訪問字符串的各個字符值:

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

for-in循環(huán)在for-in循環(huán)中進行了描述。
或者愿题,可以通過提供字符類型注釋损俭,從單個字符串文字創(chuàng)建獨立的字符常量或變量:

let exclamationMark: Character = "!"

可以通過將字符值數(shù)組作為參數(shù)傳遞給其初始值設定項來構(gòu)造字符串值:

let catCharacters: [Character] = ["C", "a", "t", "!", "??"]
let catString = String(catCharacters)
print(catString)
// Prints "Cat!??"

連接字符串和字符

可以使用加法運算符(+)將字符串值相加(或串聯(lián))以創(chuàng)建新的字符串值:

let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome now equals "hello there"

我們還可以使用加法賦值運算符(+=)將字符串值追加到現(xiàn)有字符串變量:

var instruction = "look over"
instruction += string2
// instruction now equals "look over there"

我們可以使用String類型的append() 方法將字符值追加到字符串變量:

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

注意
不能將字符串或字符追加到現(xiàn)有字符變量蛙奖,因為字符值只能包含單個字符。

如果使用多行字符串文字來構(gòu)建較長的字符串撩炊,則字符串中的每一行都會以換行符結(jié)束外永,包括最后一行。例如:

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會產(chǎn)生一個兩行字符串伯顶,這不是所需的結(jié)果。因為badStart的最后一行不會以換行符結(jié)束骆膝,所以該行會與end的第一行合并祭衩。相反,goodStart的兩行都以換行符結(jié)束阅签,因此當它與end結(jié)合時掐暮,結(jié)果有三行,正如預期的那樣政钟。

字符串插值

字符串插值是一種通過將常量路克、變量、文字和表達式的值养交,包含在字符串文字中來構(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"

在上面的示例中,multiplier的值作為 (multiplier) 插入到字符串文本中鱼辙。當計算字符串插值以創(chuàng)建實際字符串時廉嚼,此占位符將替換為乘數(shù)的實際值。
multiplier的值也是字符串后面較大表達式的一部分倒戏。此表達式計算Double(multiplier) * 2.5的值怠噪,并將結(jié)果(7.5)插入字符串。在本例中杜跷,當表達式包含在字符串文本中時傍念,它被寫為\Double(multiplier) * 2.5
可以使用擴展字符串分隔符創(chuàng)建包含字符的字符串葱椭,否則這些字符將被視為字符串插值捂寿。例如:

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

要在使用擴展分隔符的字符串中使用字符串插值,請將反斜杠后的數(shù)字符號數(shù)與字符串開頭和結(jié)尾的數(shù)字符號數(shù)匹配孵运。例如:

print(#"6 times 7 is \#(6 * 7)."#)
// Prints "6 times 7 is 42."

注意
在插入字符串的圓括號內(nèi)編寫的表達式不能包含未轉(zhuǎn)義的反斜杠(\)秦陋、回車符或換行符。但是治笨,它們可以包含其他字符串文字驳概。

Unicode碼

Unicode是一種國際標準赤嚼,用于編碼、表示和處理不同書寫系統(tǒng)中的文本顺又。它使我們能夠以標準化的形式表示任何語言中的幾乎任何字符更卒,并在文本文件或網(wǎng)頁等外部源中讀寫這些字符。Swift的字符串和字符類型完全符合Unicode稚照,如本節(jié)所述蹂空。

Unicode標量值

在后臺,Swift的原生字符串類型是從Unicode標量值構(gòu)建的果录。Unicode標量值是字符或修飾符的唯一21位數(shù)字上枕,例如U+0061表示拉丁文小寫字母A(“a”),或U+1F425表示前向小雞(“??").
請注意弱恒,并非所有21位Unicode標量值都分配給一個字符辨萍,有些標量是為將來的分配或UTF-16編碼中使用而保留的。已分配給字符的標量值通常也有一個名稱返弹,例如上面示例中的拉丁文小寫字母a和面向前方的雛雞锈玉。

擴展的圖形集簇

Swift字符類型的每個實例都表示一個擴展的grapheme集群。擴展的grapheme集群是由一個或多個Unicode標量組成的序列义起,當這些標量組合在一起時拉背,會產(chǎn)生一個人類可讀的字符。
下面是一個例子并扇。字母é可以表示為單個Unicode標量é(帶銳音符的拉丁文小寫字母E去团,或U+00E9)抡诞。但是穷蛹,同一個字母也可以表示為一對標量——一個標準字母e(拉丁文小寫字母e,或U+0065)昼汗,后跟組合的銳音符標量(U+0301)肴熏。組合的銳音符標量以圖形方式應用于它前面的標量,當它由支持Unicode的文本呈現(xiàn)系統(tǒng)呈現(xiàn)時顷窒,將e轉(zhuǎn)換為é蛙吏。
在這兩種情況下,字母é都表示為單個Swift字符值鞋吉,該值表示擴展的grapheme集群鸦做。在第一種情況下,集群包含一個標量谓着;在第二種情況下泼诱,集群包含兩個標量:

let eAcute: Character = "\u{E9}"                         // é
let combinedEAcute: Character = "\u{65}\u{301}"          // e followed by ?
// eAcute is é, combinedEAcute is e?

擴展的grapheme集群是一種靈活的方法,可以將許多復雜的腳本字符表示為單個字符值赊锚。例如治筒,韓國語字母表中的朝鮮文音節(jié)可以表示為預合成或分解的序列屉栓。在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 é?

區(qū)域指示器符號的Unicode標量可以成對組合以形成單個字符值耸袜,例如區(qū)域指示器符號字母U(U+1F1FA)域指示器符號字母S(U+1F1F8)的組合:

let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS is ????

計數(shù)字符

要檢索字符串中字符值的計數(shù)友多,請使用該字符串的count屬性:

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

請注意,Swift對字符值使用擴展的grapheme集群意味著堤框,字符串連接和修改可能并不總是會影響字符串的字符計數(shù)域滥。

例如,如果使用四個字符的單詞cafe來初始化一個新字符串蜈抓,然后在字符串末尾附加一個組合銳音符(U+0301)骗绕,則生成的字符串的字符計數(shù)仍將為4,第四個字符為e?资昧,而不是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 cafe? is 4"

注意
擴展的grapheme集群可以由多個Unicode標量組成酬土。這意味著不同的字符和同一字符的不同表示,可能需要不同數(shù)量的內(nèi)存來存儲格带。因此撤缴,Swift中的字符在字符串表示中占用的內(nèi)存量并不相同。因此叽唱,如果不遍歷字符串來確定其擴展的grapheme集群邊界的話屈呕,那么就無法計算出字符串中的字符數(shù)。如果我們使用的是特別長的字符串值棺亭,請注意count屬性必須迭代整個字符串中的Unicode標量虎眨,以便確定該字符串的字符個數(shù)。
count屬性返回的字符計數(shù)镶摘,并不總是與包含相同字符的NSString的length屬性相同嗽桩。NSString的長度基于字符串的UTF-16表示中16位編碼單元的數(shù)量,而不是字符串中Unicode擴展的grapheme集群的數(shù)量凄敢。

訪問和修改字符串

我們可以通過字符串的方法和屬性或使用下標語法來訪問和修改字符串碌冶。

字符串索引

每個字符串值都有一個關聯(lián)的索引類型,字符串索引涝缝,對應于字符串中每個字符的位置扑庞。
如上所述,不同的字符可能需要不同數(shù)量的內(nèi)存來存儲拒逮,因此為了確定哪個字符位于特定位置罐氨,必須從字符串的開始或結(jié)束處迭代每個Unicode標量。因此滩援,Swift字符串不能被整數(shù)值索引(不是很明白為什么)栅隐。
使用startIndex屬性訪問字符串第一個字符的位置。endIndex屬性是字符串中最后一個字符之后的位置。因此约啊,endIndex屬性不是字符串下標的有效參數(shù)邑遏。如果字符串為空,則startIndex和endIndex相等恰矩。
使用String的index(before:)index(after:)方法訪問給定索引之前和之后的索引记盒。要訪問距離給定索引較遠的索引,可以使用index(_:offsetBy:)方法外傅,而不是多次調(diào)用其中一個方法纪吮。
可以使用下標語法訪問特定字符串索引處的字符。

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

嘗試訪問字符串范圍之外的索引或字符串范圍之外的索引處的字符將觸發(fā)運行時錯誤萎胰。

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 ! "

注意
我們可以對遵守Collection協(xié)議的任何類型,都可以使用startIndex和endIndex屬性以及index(before:)技竟,index(after:)index(_:offsetBy:) 方法冰肴。這包括字符串,以及集合類型榔组,如數(shù)組熙尉、字典和集合。

插入和刪除

若要在指定索引處的字符串中插入單個字符搓扯,請使用 insert(_:at:) 方法检痰;若要在指定索引處插入另一個字符串的內(nèi)容,請使用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:)方法;若要刪除指定范圍內(nèi)的子字符串换可,請使用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協(xié)議的類型椎椰,都可以使用insert(_:at:)insert(contentsOf:at:)锦担、remove(at:)removeSubrange(_:)方法俭识。這包括字符串慨削,以及集合類型洞渔,如數(shù)組、字典和集合缚态。

子字符串

例如磁椒,使用下標或prefix(_:)之類的方法從字符串獲取子字符串時,結(jié)果就是表示子字符串的實例玫芦,而不是另一個字符串浆熔。Swift中的子字符串具有與字符串大多數(shù)相同的方法,這意味著我們可以使用與處理字符串相同的方法處理子字符串桥帆。但是医增,與字符串不同的是慎皱,在對字符串執(zhí)行操作時,得到的子字符串的存儲使用時間很短叶骨。如果我們準備將結(jié)果存儲更長的時間時茫多,可以將子字符串轉(zhuǎn)換為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)

與字符串一樣忽刽,每個子字符串都有一個存儲子字符串的字符的內(nèi)存區(qū)域天揖。字符串和子字符串之間的區(qū)別在于,從性能優(yōu)化的角度來看跪帝,子字符串可以重用存儲原始字符串的部分內(nèi)存今膊,或存儲另一個子字符串的部分內(nèi)存。(字符串具有類似的優(yōu)化伞剑,但如果兩個字符串共享內(nèi)存斑唬,則它們相等。)這個性能優(yōu)化意味著我們不必為復制內(nèi)存耗費性能成本黎泣,直到我們修改字符串或子字符串赖钞。如上所述,子字符串不適合長期存儲聘裁,因為它們重用了原始字符串的存儲雪营,所以只要使用任何子字符串,整個原始字符串都必須保存在內(nèi)存中衡便。
在上面的例子中献起,greeting是一個字符串,這意味著它有一個存儲組成字符串的字符的內(nèi)存區(qū)域镣陕。因為開頭是greeting的子字符串谴餐,所以它重用了greeting使用的內(nèi)存。相反呆抑,newString是一個由子字符串創(chuàng)建的字符串岂嗓,它有自己的存儲空間。下圖顯示了這些關系:

截屏2021-02-03 下午5.33.54.png

注意
字符串和子字符串都遵守StringProtocol協(xié)議鹊碍,這意味著字符串操作函數(shù)通逞嵫常可以方便地接受StringProtocol值〕薰荆可以使用字符串或子字符串值調(diào)用此類函數(shù)公罕。

比較字符串

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")
}
// Prints "These two strings are considered equal"

如果兩個字符串值(或兩個字符值)的擴展grapheme簇在規(guī)范上是等價的,則認為它們相等。如果擴展的字組簇具有相同的語言含義和外觀罐柳,則它們在規(guī)范上是等價的掌腰,即使它們是由不同的Unicode標量在幕后組成的。

例如张吉,帶銳音符(U+00E9)的拉丁文小寫字母E在規(guī)范上等同于拉丁文小寫字母E(U+0065)后跟組合銳音符(U+0301)辅斟。這兩個擴展的字組簇都是表示字符的有效方法,因此它們被認為是規(guī)范等效的:

// "Voulez-vous un café?" using LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"

// "Voulez-vous un cafe??" 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中的字符串和字符比較不區(qū)分區(qū)域設置。

前綴和后綴相等

若要檢查字符串是否具有特定的字符串前綴或后綴缓苛,請調(diào)用該字符串的hasPrefix(_:)hasSuffix(_:)方法芳撒,這兩種方法都采用了單個string類型參數(shù)并返回布爾值。
下面的例子考慮的是一組字符串未桥,表示莎士比亞《羅密歐與朱麗葉》前兩幕的場景位置:

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數(shù)組來計算戲劇第一幕中的場景數(shù):

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(_:)方法計算發(fā)生在卡普萊特的豪宅和勞倫斯修士牢房內(nèi)或周圍的場景數(shù)量:

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集群之間執(zhí)行逐字符的規(guī)范等價比較,如字符串和字符相等中所述冬耿。

字符串的Unicode表示

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

Swift提供了幾種不同的方法來訪問字符串的Unicode表示缤骨“В可以使用for-in語句對字符串進行迭代,以將其單個字符值作為Unicode擴展的grapheme集群進行訪問绊起。在“使用字符”中介紹了此過程精拟。

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

  • UTF-8編碼單元的集合(使用字符串的utf8屬性訪問)
  • UTF-16編碼單元的集合(使用字符串的utf16屬性訪問)
  • 21位Unicode標量值的集合虱歪,相當于字符串的UTF-32編碼形式(使用字符串的unicodeScalars屬性訪問)

下面的每個示例都顯示了以下字符串的不同表示形式蜂绎,該字符串由字符D、o实蔽、g荡碾、?(雙感嘆號,或Unicode標量U+203C)和?? 字符(狗臉局装,或Unicode標量U+1F436)組成:

let dogString = "Dog???"

UTF-8表示法

我們可以通過迭代字符串的utf8屬性來訪問它的UTF-8表示形式。此屬性的類型為String.UTF8View,它是無符號8位(UInt8)值的集合铐尚,字符串的UTF-8表示形式中的每個字節(jié)對應一個值:

截屏2021-02-03 下午5.40.08.png

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

在上面的示例中拨脉,前三個十進制codeUnit值(68111103)表示字符D、o和g宣增,其UTF-8表示形式與其ASCII表示形式相同玫膀。接下來的三個十進制codeUnit值(226、128爹脾、188)是雙感嘆號字符的三字節(jié)UTF-8表示帖旨。最后四個codeUnit值(240、159灵妨、144解阅、182)是狗臉字符的四字節(jié)UTF-8表示。

UTF-16表示法

通過迭代字符串的utf16屬性泌霍,可以訪問字符串的UTF-16表示形式货抄。此屬性的類型為String.UTF16View,它是無符號16位(UInt16)值的集合朱转,在字符串的UTF-16表示形式中蟹地,每個16位編碼單元對應一個值:


截屏2021-02-03 下午5.45.07.png
for codeUnit in dogString.utf16 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 55357 56374 "

同樣,前三個codeUnit值(68藤为、111怪与、103)表示字符D、o和g缅疟,它們的UTF-16代碼單元的值與字符串的UTF-8表示中的值相同(因為這些Unicode標量表示ASCII字符)琼梆。
第四個codeUnit值(8252)是十六進制值203C的十進制等價值,它表示雙感嘆號字符的Unicode標量U+203C窿吩。在UTF-16中茎杂,這個字符可以表示為單個編碼單元。
第五和第六個codeUnit值(55357和56374)是狗臉字符的UTF-16的表示對纫雁。這些值是高位表示U+D83D(十進制值55357)和低位表示U+DC36(十進制值56374)煌往。

Unicode標量表示法

我們可以通過迭代字符串值的 unicodeScalars 屬性來訪問字符串值的Unicode標量表示。此屬性的類型為UnicodeScalarView轧邪,它是UnicodeScalar類型的值的集合刽脖。
每個UnicodeScalar都有一個value屬性,返回標量的21位值忌愚,用UInt32值表示:


截屏2021-02-03 下午5.47.58.png
for scalar in dogString.unicodeScalars {
    print("\(scalar.value) ", terminator: "")
}
print("")
// Prints "68 111 103 8252 128054 "

前三個UnicodeScalar值(68, 111, 103)的value屬性曲管,還是表示字符D、o和g硕糊。
第四個codeUnit值(8252)也是十六進制值203C的十進制等價值院水,它表示雙感嘆號字符的Unicode標量U+203C腊徙。
第五個也是最后一個UnicodeScalar 128054的value屬性是十六進制值1F436的十進制等價值,它表示狗臉字符的Unicode標量U+1F436檬某。
作為查詢其值屬性的替代方法撬腾,每個UnicodeScalar值也可用于構(gòu)造新的字符串值,例如使用字符串插值:

for scalar in dogString.unicodeScalars {
    print("\(scalar) ")
}
// D
// o
// g
// ?
// ??
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恢恼,一起剝皮案震驚了整個濱河市民傻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌场斑,老刑警劉巖漓踢,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異漏隐,居然都是意外死亡喧半,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門锁保,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薯酝,“玉大人,你說我怎么就攤上這事爽柒∥獠ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵浩村,是天一觀的道長做葵。 經(jīng)常有香客問我,道長心墅,這世上最難降的妖魔是什么酿矢? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮怎燥,結(jié)果婚禮上瘫筐,老公的妹妹穿的比我還像新娘。我一直安慰自己铐姚,他們只是感情好策肝,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著隐绵,像睡著了一般之众。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上依许,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天棺禾,我揣著相機與錄音,去河邊找鬼峭跳。 笑死膘婶,一個胖子當著我的面吹牛缺前,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播竣付,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼诡延,長吁一口氣:“原來是場噩夢啊……” “哼滞欠!你這毒婦竟也來了古胆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤筛璧,失蹤者是張志新(化名)和其女友劉穎逸绎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夭谤,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡棺牧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了朗儒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颊乘。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖醉锄,靈堂內(nèi)的尸體忽然破棺而出乏悄,到底是詐尸還是另有隱情,我是刑警寧澤恳不,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布檩小,位于F島的核電站,受9級特大地震影響烟勋,放射性物質(zhì)發(fā)生泄漏规求。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一卵惦、第九天 我趴在偏房一處隱蔽的房頂上張望阻肿。 院中可真熱鬧,春花似錦沮尿、人聲如沸丛塌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至贩幻,卻和暖如春庸疾,著一層夾襖步出監(jiān)牢的瞬間乍楚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工届慈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留徒溪,地道東北人忿偷。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像臊泌,于是被迫代替她去往敵國和親鲤桥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容