基于Swift 版本 5.1
// 一個(gè)Unicode字符串值心软,它是字符的集合娩怎。
struct String
字符串是一系列字符組成的集合爆阶,如“Swift”沃测。Swift中的字符串是Unicode正確且對(duì)語(yǔ)言環(huán)境不敏感的斥扛,旨在提高效率入问。字符串類型與Objective-C類NSString連接,并提供與處理字符串的C函數(shù)的互操作性稀颁。
您可以使用字符串文字或字符串內(nèi)插來(lái)創(chuàng)建新字符串芬失。字符串文字是用引號(hào)括起來(lái)的一系列字符。
Swift的String類型是一種值類型,String值再傳遞給方法或者函數(shù)的時(shí)候,會(huì)被復(fù)制過(guò)去,還有賦值給常量或者變量的時(shí)候也一樣匾灶。每一次賦值和傳遞,現(xiàn)存的String值都會(huì)被復(fù)制一次,傳遞走的是拷貝而不是原本棱烂。Swift編譯器優(yōu)化了字符串使用的資源,實(shí)際上拷貝只會(huì)再確實(shí)需要的時(shí)候才進(jìn)行。
字符串插值是字符串文字阶女,用于評(píng)估所有包含的表達(dá)式并將結(jié)果轉(zhuǎn)換為字符串形式颊糜。字符串插值為您提供了一種由多個(gè)片段構(gòu)建字符串的簡(jiǎn)便方法。將每個(gè)表達(dá)式括在括號(hào)內(nèi)的字符串內(nèi)插中秃踩,并以反斜杠為前綴衬鱼。
let name = "Rosa"
let personalizedGreeting = "Welcome, \(name)!"
// personalizedGreeting == "Welcome, Rosa!"
let price = 2
let number = 3
let cookiePrice = "\(number) cookies: $\(price * number)."
// cookiePrice == "3 cookies: $6."
使用串聯(lián)運(yùn)算符(+)合并字符串。
let longerGreeting = greeting + " We're glad you're here!"
// longerGreeting == "Welcome! We're glad you're here!"
多行字符串文字用三個(gè)雙引號(hào)(""")括起來(lái)憔杨,每個(gè)定界符在其自己的行上鸟赫。縮進(jìn)從多行字符串文字的每一行中去除消别,以匹配結(jié)束定界符的縮進(jìn)抛蚤。
let banner = """
a\
b
c
d
"""
print(banner)
// log
/*
ab
c
d
*/
修改和比較字符串
字符串始終具有值語(yǔ)義。修改字符串的副本不會(huì)影響原始字符串寻狂。
var otherGreeting = greeting
otherGreeting += " Have a nice time!"
// otherGreeting == "Welcome! Have a nice time!"
print(greeting)
// Prints "Welcome!"
使用Unicode規(guī)范表示來(lái)使用等于運(yùn)算符(==)或關(guān)系運(yùn)算符(如<或>=)比較字符串是否相等岁经。結(jié)果,字符串的不同表示形式比較相等蛇券。
let cafe1 = "Cafe\u{301}"
let cafe2 = "Café"
print(cafe1 == cafe2)
// Prints "true"
Unicode標(biāo)量值"\u{301}"修改前面的字符以包含重音符號(hào)蒿偎,因此"e\u{301}"具有與單個(gè)Unicode標(biāo)量值相同的規(guī)范表示形式"é"。
基本的字符串操作對(duì)語(yǔ)言環(huán)境設(shè)置不敏感怀读,可確保字符串比較和其他操作始終具有單個(gè)穩(wěn)定的結(jié)果诉位,從而允許將字符串用作Dictionary實(shí)例中的鍵或用于其他目的。
訪問(wèn)字符串元素
字符串是擴(kuò)展的字素簇的集合菜枷,這些簇近似于人類可讀的字符苍糠。許多單獨(dú)的字符(例如“é”,“?”和“????”)可以由多個(gè)Unicode標(biāo)量值組成啤誊。這些標(biāo)量值由Unicode的邊界算法組合成擴(kuò)展的字形簇岳瞭,由Swift Character類型表示拥娄。字符串的每個(gè)元素都由一個(gè)Character實(shí)例表示。
例如瞳筏,要檢索較長(zhǎng)字符串的第一個(gè)單詞稚瘾,可以搜索一個(gè)空格,然后從該字符串的前綴創(chuàng)建一個(gè)子字符串姚炕,直到該點(diǎn)為止:
let name = "Marie Curie"
let firstSpace = name.firstIndex(of: " ") ?? name.endIndex
let firstName = name[..<firstSpace]
// firstName == "Marie"
該常數(shù)是實(shí)例摊欠,表示一個(gè)字符串的子串,同時(shí)共享原始字符串的存儲(chǔ)類型柱宦,一種類型些椒。子字符串與字符串具有相同的接口。
print("\(name)'s first name has \(firstName.count) letters.")
// Prints "Marie Curie's first name has 5 letters."
訪問(wèn)字符串的Unicode表示形式
如果你需要在不同的Unicode編碼掸刊,字符串的使用一個(gè)編碼訪問(wèn)字符串的內(nèi)容免糕,或性質(zhì)。每個(gè)屬性都以一系列代碼單元的形式提供對(duì)字符串視圖的訪問(wèn)忧侧,每個(gè)代碼單元均以不同的Unicode編碼進(jìn)行編碼石窑。unicodeScalars、utf16蚓炬、utf8
為了演示每個(gè)字符串可用的不同視圖松逊,以下示例使用此String實(shí)例:
let cafe = "Cafe\u{301} du ??"
print(cafe)
// Prints "Café du ??"
該cafe字符串是九個(gè)字符顯示的字符串時(shí)可見(jiàn)的集合。
print(cafe.count)
// Prints "9"
print(Array(cafe))
// Prints "["C", "a", "f", "é", " ", "d", "u", " ", "??"]"
Unicode標(biāo)量視圖
字符串的unicodeScalars屬性是Unicode標(biāo)量值的集合试吁,21位代碼是Unicode的基本單位。每個(gè)標(biāo)量值都用Unicode表示楼咳。標(biāo)量實(shí)例熄捍,相當(dāng)于一個(gè)UTF-32代碼單元。
print(cafe.unicodeScalars.count)
// Prints "10"
print(Array(cafe.unicodeScalars))
// Prints "["C", "a", "f", "e", "\u{0301}", " ", "d", "u", " ", "\u{0001F30D}"]"
print(cafe.unicodeScalars.map { $0.value })
// Prints "[67, 97, 102, 101, 769, 32, 100, 117, 32, 127757]"
unicodeScalars視圖的元素包含cafe字符串中的每個(gè)Unicode標(biāo)量值母怜。特別是余耽,因?yàn)閏afe是使用“é”字符的分解形式聲明的,所以u(píng)nicodeScalars包含字母“e”(101)和重音字符“’”(769)的標(biāo)量值苹熏。
UTF-16視圖
字符串的utf16屬性是UTF-16編碼單元的集合碟贾,這是字符串的Unicode標(biāo)量值的16位編碼形式。每個(gè)代碼單元存儲(chǔ)為UInt16實(shí)例轨域。
print(cafe.utf16.count)
// Prints "11"
print(Array(cafe.utf16))
// Prints "[67, 97, 102, 101, 769, 32, 100, 117, 32, 55356, 57101]"
utf16視圖的元素是UTF-16編碼時(shí)字符串的代碼單元袱耽。這些元素與通過(guò)索引的NSString api訪問(wèn)的元素相匹配。
let nscafe = cafe as NSString
print(nscafe.length)
// Prints "11"
print(nscafe.character(at: 3))
// Prints "101"
UTF-8視圖
字符串的utf8屬性是UTF-8代碼單元的集合干发,這是字符串的Unicode標(biāo)量值的8位編碼形式朱巨。每個(gè)代碼單元存儲(chǔ)為一個(gè)UInt8實(shí)例。
print(cafe.utf8.count)
// Prints "14"
print(Array(cafe.utf8))
// Prints "[67, 97, 102, 101, 204, 129, 32, 100, 117, 32, 240, 159, 140, 141]"
utf8視圖的元素是UTF-8編碼時(shí)字符串的代碼單元枉长。此表示與將字符串實(shí)例傳遞給C api時(shí)使用的表示相匹配冀续。
let cLength = strlen(cafe)
print(cLength)
// Prints "14"
測(cè)量字符串長(zhǎng)度
當(dāng)您需要知道一個(gè)字符串的長(zhǎng)度時(shí)琼讽,您必須首先考慮將該長(zhǎng)度用于什么。您是在度量將顯示在屏幕上的字符數(shù)洪唐,還是在度量特定編碼中字符串所需的存儲(chǔ)空間?當(dāng)通過(guò)不同的視圖測(cè)量時(shí)钻蹬,一個(gè)字符串的長(zhǎng)度可能會(huì)有很大的差異。
例如凭需,像大寫字母A這樣的ASCII字符由它的四個(gè)視圖中的每個(gè)元素表示问欠。A的Unicode標(biāo)量值是65,它足夠小功炮,可以同時(shí)容納UTF-16和UTF-8中的單個(gè)代碼單元溅潜。
let capitalA = "A"
print(capitalA.count)
// Prints "1"
print(capitalA.unicodeScalars.count)
// Prints "1"
print(capitalA.utf16.count)
// Prints "1"
print(capitalA.utf8.count)
// Prints "1"
另一方面,表情符號(hào)標(biāo)志字符由一對(duì)Unicode標(biāo)量值(如"\u{1F1F5}"和)構(gòu)成"\u{1F1F7}"薪伏。這些標(biāo)量值中的每一個(gè)都太大滚澜,以致無(wú)法放入單個(gè)UTF-16或UTF-8代碼單元中。結(jié)果嫁怀,字符串的每個(gè)視圖"????"報(bào)告的長(zhǎng)度都不同设捐。
let flag = "????"
print(flag.count)
// Prints "1"
print(flag.unicodeScalars.count)
// Prints "2"
print(flag.utf16.count)
// Prints "4"
print(flag.utf8.count)
// Prints "8"
要檢查字符串是否為空,請(qǐng)使用其isEmpty屬性塘淑,而不是將其中一個(gè)視圖的長(zhǎng)度與0進(jìn)行比較萝招。與isEmpty不同,計(jì)算視圖的count屬性需要遍歷字符串的元素存捺。
訪問(wèn)字符串視圖元素
要查找字符串的各個(gè)元素槐沼,請(qǐng)為任務(wù)使用適當(dāng)?shù)囊晥D。例如捌治,要檢索較長(zhǎng)字符串的第一個(gè)單詞岗钩,您可以搜索該字符串的空格,然后從該字符串的前綴到該點(diǎn)創(chuàng)建一個(gè)新字符串肖油。
let name = "Marie Curie"
let firstSpace = name.firstIndex(of: " ") ?? name.endIndex
let firstName = name[..<firstSpace]
print(firstName)
// Prints "Marie"
字符串及其視圖共享索引兼吓,因此可以使用相同的firstSpace索引訪問(wèn)名稱字符串的UTF-8視圖。
print(Array(name.utf8[..<firstSpace]))
// Prints "[77, 97, 114, 105, 101]"
注意森枪,一個(gè)視圖的索引在另一個(gè)視圖中可能沒(méi)有確切的對(duì)應(yīng)位置视搏。例如,上面聲明的標(biāo)記字符串包含一個(gè)字符县袱,但是當(dāng)編碼為UTF-8時(shí)浑娜,它由8個(gè)代碼單元組成。下面的代碼為標(biāo)記中的第一個(gè)和第二個(gè)位置創(chuàng)建常量式散。use utf8視圖棚愤。使用這些索引訪問(wèn)utf8視圖將生成第一個(gè)和第二個(gè)代碼UTF-8單元。
let firstCodeUnit = flag.startIndex
let secondCodeUnit = flag.utf8.index(after: firstCodeUnit)
// flag.utf8[firstCodeUnit] == 240
// flag.utf8[secondCodeUnit] == 159
但是,當(dāng)用來(lái)訪問(wèn)標(biāo)記字符串本身的元素時(shí)宛畦,secondCodeUnit索引并不對(duì)應(yīng)于特定字符的位置瘸洛。不是只訪問(wèn)特定的UTF-8代碼單元,而是將該索引視為字符在索引編碼偏移處的位置次和。在secondCodeUnit的情況下反肋,該字符仍然是標(biāo)記本身。
如果需要驗(yàn)證一個(gè)字符串視圖中的索引是否與另一個(gè)視圖中的確切位置相對(duì)應(yīng)踏施,請(qǐng)使用索引的samePosition(in:)方法或init(_:within:)初始化器石蔗。
if let exactIndex = secondCodeUnit.samePosition(in: flag) {
print(flag[exactIndex])
} else {
print("No exact match for this position.")
}
// Prints "No exact match for this position."
性能優(yōu)化
盡管Swift中的字符串具有值語(yǔ)義,但字符串使用寫時(shí)復(fù)制策略將數(shù)據(jù)存儲(chǔ)在緩沖區(qū)中畅形。然后养距,這個(gè)緩沖區(qū)可以由字符串的不同副本共享。一個(gè)字符串的數(shù)據(jù)只有在多個(gè)字符串實(shí)例使用同一個(gè)緩沖區(qū)時(shí)日熬,才會(huì)在發(fā)生變化時(shí)惰性地復(fù)制棍厌。因此,任何順序的第一個(gè)突變操作可能會(huì)花費(fèi)O(n)時(shí)間和空間竖席。
當(dāng)字符串的連續(xù)存儲(chǔ)被填滿時(shí)耘纱,必須分配一個(gè)新的緩沖區(qū),并且必須將數(shù)據(jù)移動(dòng)到新的存儲(chǔ)中毕荐。字符串緩沖區(qū)使用指數(shù)增長(zhǎng)策略束析,當(dāng)對(duì)多個(gè)附加操作求平均值時(shí),該策略使附加到字符串成為常數(shù)時(shí)間操作憎亚。
字符串和NSString之間的橋接
任何字符串實(shí)例都可以使用類型轉(zhuǎn)換操作符(as)橋接到NSString员寇,而來(lái)自O(shè)bjective-C的任何字符串實(shí)例都可以使用NSString實(shí)例作為其存儲(chǔ)。因?yàn)镹SString的任意子類都可以成為一個(gè)字符串實(shí)例第美,所以當(dāng)一個(gè)字符串實(shí)例由NSString存儲(chǔ)支持時(shí)蝶锋,不能保證它的表示形式或效率。因?yàn)镹SString是不可變的斋日,它就像存儲(chǔ)被一個(gè)拷貝共享一樣牲览。任何序列中的第一個(gè)操作都會(huì)導(dǎo)致元素被復(fù)制到唯一的墓陈、連續(xù)的存儲(chǔ)中恶守,這可能會(huì)消耗O(n)時(shí)間和空間,其中n是字符串的編碼表示長(zhǎng)度(或者更多贡必,如果底層NSString具有不尋常的性能特征)兔港。
有關(guān)本討論中使用的Unicode術(shù)語(yǔ)的更多信息,請(qǐng)參見(jiàn)Unicode.org詞匯表仔拟。特別是衫樊,本文討論了擴(kuò)展的grapheme集群、Unicode標(biāo)量值和規(guī)范等價(jià)。