当前位置: 首页>>编程示例 >>用法及示例精选 >>正文


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。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。