當前位置: 首頁>>編程示例 >>用法及示例精選 >>正文


Swift String用法及代碼示例

結構

String

一個 Unicode 字符串值,它是字符的集合。

聲明

@frozen struct String

概述

字符串是形成集合的一係列字符,例如 "Swift" 。 Swift 中的字符串是 Unicode 正確且不區分區域設置的,並且被設計為高效。 String 類型與 Objective-C 類 NSString 橋接,並提供與使用字符串的 C 函數的互操作性。

您可以使用字符串文字或字符串插值創建新字符串。 string literal 是用引號括起來的一係列字符。


let greeting = "Welcome!"

String interpolations 是字符串文字,用於評估任何包含的表達式並將結果轉換為字符串形式。字符串插值為您提供了一種從多個片段構建字符串的簡單方法。將每個表達式包裝在括號中的字符串插值中,並以反斜杠為前綴。


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

使用連接運算符 (+) 組合字符串。


let longerGreeting = greeting + " We're glad you're here!"
// longerGreeting == "Welcome! We're glad you're here!"

多行字符串文字用三個雙引號 (""") 括起來,每個分隔符在自己的行上。從多行字符串文字的每一行中去除縮進以匹配結束分隔符的縮進。


let banner = """
          __,
         (           o  /) _/_
          `.  , , , ,  //  /
        (___)(_(_/_(_ //_ (__
                     /)
                    (/
        """

修改和比較字符串

字符串總是具有值語義。修改字符串的副本不會影響原始字符串。


var otherGreeting = greeting
otherGreeting += " Have a nice time!"
// otherGreeting == "Welcome! Have a nice time!"


print(greeting)
// Prints "Welcome!"

使用等於運算符 (==) 或關係運算符(如 <>= )比較字符串是否相等始終使用 Unicode 規範表示。結果,字符串的不同表示比較相等。


let cafe1 = "Cafe\u{301}"
let cafe2 = "Café"
print(cafe1 == cafe2)
// Prints "true"

Unicode 標量值 "\u{301}" 將前麵的字符修改為包含重音符號,因此 "e\u{301}" 與單個 Unicode 標量值 "é" 具有相同的規範表示。

基本字符串操作對區域設置不敏感,確保字符串比較和其他操作始終具有單一、穩定的結果,允許將字符串用作 Dictionary 實例中的鍵和用於其他目的。

訪問字符串元素

字符串是 extended grapheme clusters 的集合,它近似於人類可讀的字符。許多單獨的字符,例如 “é”、“김” 和 “🇮🇳”,可以由多個 Unicode 標量值組成。這些標量值由 Unicode 的邊界算法組合成擴展的字素簇,由 Swift Character 類型表示。字符串的每個元素都由 Character 實例表示。

例如,要檢索較長字符串的第一個單詞,您可以搜索一個空格,然後從該字符串的前綴創建一個子字符串:


let name = "Marie Curie"
let firstSpace = name.firstIndex(of: " ") ?? name.endIndex
let firstName = name[..<firstSpace]
// firstName == "Marie"

firstName 常量是 Substring 類型的一個實例,該類型表示字符串的子字符串,同時共享原始字符串的存儲空間。子字符串呈現與字符串相同的接口。


print("\(name)'s first name has \(firstName.count) letters.")
// Prints "Marie Curie's first name has 5 letters."

訪問字符串的 Unicode 表示

如果您需要訪問以不同 Unicode 編碼編碼的字符串的內容,請使用字符串的 unicodeScalarsutf16utf8 屬性之一。每個屬性都提供對作為一係列代碼單元的字符串視圖的訪問,每個代碼單元都以不同的 Unicode 編碼進行編碼。

為了演示可用於每個字符串的不同視圖,以下示例使用此 String 實例:


let cafe = "Cafe\u{301} du 🌍"
print(cafe)
// Prints "Café du 🌍"

cafe 字符串是顯示字符串時可見的九個字符的集合。


print(cafe.count)
// Prints "9"
print(Array(cafe))
// Prints "["C", "a", "f", "é", " ", "d", "u", " ", "🌍"]"

Unicode 標量視圖

字符串的 unicodeScalars 屬性是 Unicode 標量值的集合,21 位代碼是 Unicode 的基本單位。每個標量值由一個Unicode.Scalar 實例表示,相當於一個 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 字符串中的每個 Unicode 標量值。特別是,因為cafe 是使用"é" 字符的分解形式聲明的,所以unicodeScalars 包含字母"e" (101) 和重音字符"´" (769) 的標量值。

UTF-16 視圖

字符串的utf16 屬性是 UTF-16 代碼單元的集合,即字符串的 Unicode 標量值的 16 位編碼形式。每個代碼單元都存儲為 UInt16 實例。


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 編碼時的代碼單元。這些元素與通過索引NSString API 訪問的元素相匹配。


let nscafe = cafe as NSString
print(nscafe.length)
// Prints "11"
print(nscafe.character(at: 3))
// Prints "101"

UTF-8 視圖

字符串的utf8 屬性是 UTF-8 代碼單元的集合,即字符串 Unicode 標量值的 8 位編碼形式。每個代碼單元都存儲為 UInt8 實例。


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 編碼時的代碼單元。此表示與 String 實例傳遞給 C API 時使用的表示相匹配。


let cLength = strlen(cafe)
print(cLength)
// Prints "14"

測量字符串的長度

當您需要知道字符串的長度時,您必須首先考慮將使用該長度的目的。您是在測量將在屏幕上顯示的字符數,還是在測量特定編碼中的字符串所需的存儲量?當通過不同的視圖測量時,單個字符串的長度可能會有很大差異。

例如,像大寫字母A 這樣的 ASCII 字符在其四個視圖中的每一個視圖中都由一個元素表示。 A 的 Unicode 標量值是 65 ,它足夠小以適合 UTF-16 和 UTF-8 中的單個代碼單元。


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"

另一方麵,表情符號標誌字符由一對 Unicode 標量值構成,例如 "\u{1F1F5}""\u{1F1F7}" 。反過來,這些標量值中的每一個都太大而無法放入單個 UTF-16 或 UTF-8 代碼單元中。因此,字符串"🇵🇷" 的每個視圖都會報告不同的長度。


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"

要檢查字符串是否為空,請使用其 isEmpty 屬性,而不是將其中一個視圖的長度與 0 進行比較。與 isEmpty 不同,計算視圖的 count 屬性需要遍曆字符串的元素。

訪問字符串視圖元素

要查找字符串的各個元素,請使用適合您任務的視圖。例如,要檢索較長字符串的第一個單詞,您可以在字符串中搜索空格,然後從該字符串的前綴創建一個新字符串。


let name = "Marie Curie"
let firstSpace = name.firstIndex(of: " ") ?? name.endIndex
let firstName = name[..<firstSpace]
print(firstName)
// Prints "Marie"

字符串及其視圖共享索引,因此您可以使用相同的 firstSpace 索引訪問 name 字符串的 UTF-8 視圖。


print(Array(name.utf8[..<firstSpace]))
// Prints "[77, 97, 114, 105, 101]"

請注意,一個視圖的索引在另一個視圖中可能沒有精確的對應位置。例如,上麵聲明的flag 字符串包含單個字符,但在編碼為 UTF-8 時由八個代碼單元組成。以下代碼為flag.utf8 視圖中的第一個和第二個位置創建常量。使用這些索引訪問 utf8 視圖會生成第一個和第二個代碼 UTF-8 單元。


let firstCodeUnit = flag.startIndex
let secondCodeUnit = flag.utf8.index(after: firstCodeUnit)
// flag.utf8[firstCodeUnit] == 240
// flag.utf8[secondCodeUnit] == 159

但是,當用於訪問flag 字符串本身的元素時,secondCodeUnit 索引不對應於特定字符的位置。該索引不隻是訪問特定的 UTF-8 代碼單元,而是被視為字符在索引編碼偏移處的位置。在 secondCodeUnit 的情況下,該字符仍然是標誌本身。


// flag[firstCodeUnit] == "🇵🇷"
// flag[secondCodeUnit] == "🇵🇷"

如果您需要驗證一個字符串視圖中的索引是否與另一個視圖中的確切位置相對應,請使用索引的 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."

性能優化

盡管 Swift 中的字符串具有值語義,但字符串使用 copy-on-write 策略將其數據存儲在緩衝區中。然後,該緩衝區可以由字符串的不同副本共享。當多個字符串實例使用同一個緩衝區時,字符串的數據隻會在突變時延遲複製。因此,任何變異操作序列中的第一個可能會花費 O(n) 時間和空間。

當字符串的連續存儲空間被填滿時,必須分配一個新的緩衝區,並且必須將數據移動到新的存儲空間。字符串緩衝區使用指數增長策略,當對許多追加操作進行平均時,該策略使追加到字符串成為恒定時間操作。

String 和 NSString 之間的橋接

任何String 實例都可以使用type-cast 運算符(as) 橋接到NSString,並且源自Objective-C 的任何String 實例都可以使用NSString 實例作為其存儲。因為NSString 的任意子類都可以成為String 實例,所以當String 實例由NSString 存儲支持時,無法保證表示或效率。因為NSString 是不可變的,就好像存儲是由一個副本共享的。任何變異操作序列中的第一個會導致元素被複製到唯一的連續存儲中,這可能會花費 O(n) 時間和空間,其中 n 是字符串編碼表示的長度(或者更多,如果底層NSString 具有不尋常的性能特征)。

有關此討論中使用的 Unicode 術語的更多信息,請參閱Unicode.org glossary。特別是,此討論提到了 extended grapheme clustersUnicode scalar valuescanonical equivalence

可用版本

iOS 8.0+, iPadOS 8.0+, macOS 10.10+, Mac Catalyst 13.0+, tvOS 9.0+, watchOS 2.0+

相關用法


注:本文由純淨天空篩選整理自apple.com大神的英文原創作品 String。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。