字符串和字符

未匹配的标注

字符串是由一系列字符组成,比如 「hello, world」 或 「albatross」 。 Swift 字符串是由 String 类型来表示。 String 的内容可以用多种方式读取,包括作为一个 Character 值的集合。

Swift 的 StringCharacter 类型提供了一个快速的和兼容 Unicode 的方式供你的代码使用。创建和处理字符串的语法是轻量且易读的,并且提供了一种和 C 语言相似的字面量语法。字符串拼接只需要通过简单的 + 操作符将两个字符串结合起来即可,像 Swift 中其它值一样,字符串的可变性取决于它是常量还是变量。你还可以在字符串插值过程中将字符串常量、变量、字面量和表达式插入到一个更长的字符串中。这样可以更容易的创建一个自定义的字符串,用于展示、存储和打印。

尽管语法很简单, Swift 的 String 类型却是一个快速的、现代的字符串实现。每个字符串都是由编码无关的 Unicode 字符组成,并且支持多种 Unicode 的表示形式访问字符。

注意
Swift 的字符串类型与 Foundation 的 NSString 类型进行了无缝桥接。 Foundation 也可以对 String 进行扩展,暴露在 NSString 中定义的方法。 这就意味着,你可以不用进行类型转换,就能在 String 中调用 NSString 的这些方法。
更多关于在 Foundation 和 Cocoa 中使用 String 的信息,查看  Bridging Between String and NSString


字符串字面量

您可以在代码中预定义 String 的值作为字符串类型,字符串文字是由双引号(")包围的字符序列。

使用字符串文字作为常量或变量的初始值:

let someString = "Some string literal value"

需要注意的是在 Swift 中对 someString 的声明为 String 类型,是因为初始赋值为 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."
"""

一个多行字符串字面量包含了从开启到关闭引号之间所有的行。这个字符串是从开启引号 ( """ ) 之后的第一行开始的,并在关闭引号之前的最后一行结束。 这就意味着下面的两个字符串在开启引号之后和关闭引号之前都没有换行符号:

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 编译器其他各行那些空白字符都需要忽略。然而,如果你在一行的开头写了空白字符,除了关闭引号(""")之前的那些空白字符,这些空白字符都被 包含 在多行字符串文字中。

0

在上面的例子中,即使整个多行字符串文字都是缩进的,但第一行和最后一行也没以任何空格开头。中间一行的缩进超过关闭引号(""")之前的空白字符,所以,这一行以四个空格缩进开始。

字符串中的特殊字符

字符串可以包含如下特殊字符:

  • 转义特殊字符 \0 (空字符)、 \\ (反斜线字符)、 \t (水平制表符)、 \n (换行)、 \r (回车)、\" (双引号) 和 \' (单引号)
  • 任意的 Unicode 字符,记作 \u{n}n  是一个 1--8 位十六进制数,它的值等同于一个有效的 Unicode 码点 (下面是 Unicode 编码的介绍  Unicode )

下面是这些特殊字符的四个例子。 wiseWords 是一个包含了两个双引号常量。 dollarSignblackHeart 和 sparklingHeart 都是 Unicode 编码格式的常量:

let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein
let dollarSign = "\u{24}"        // $,  Unicode 编码是 U+0024
let blackHeart = "\u{2665}"      // ♥,  Unicode 编码是 U+2665
let sparklingHeart = "\u{1F496}" // 💖, Unicode 编码是 U+1F496

因为使用三个双引号来表示多行字符串, 在多行字符串中使用一个双引号 ( " ) 无需转义。但是要包含这样的文体 """ 至少要对其中一个进行转义。例如:

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


初始化一个空字符串

创建一个空 String 有两种方式,给一个变量赋值一个空字符串或者使用下面的语法初始化一个 String 实例对象:

var emptyString = ""               // 空字符串
var anotherEmptyString = String()  // 初始化语法
//这是两个空字符串,他们等价

可以通过检查 String 的布尔类型的属性 isEmpty 来判断该字符串的值是否为空:

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


可变字符串

你可以通过给一个变量赋值(在这种情况下可以被修改)或者给一个常量赋值(这种情况下不能被修改)判断一个给定的 String 是否可修改(或者叫 突变):

var variableString = "Horse"
variableString += " and carriage"
// 现在 variableString 的值是 "Horse and carriage"

let constantString = "Highlander"
constantString += " and another Highlander"
// 编译报错 - 一个常量字符串不能被修改

注意

这种方法与 Objective-C 和 Cocoa 中的可变字符串不一样,你可以使用(NSStringNSMutableString)这两个类来申明一个字符串是否可变。


字符串是值类型

Swift 中的 String  类型是一种 值类型 。如果你创建了一个新的 String 值, String 值在传递给方法或者函数时会被 拷贝,在给常量或者变量赋值的时候也是一样。在任何情况下,都会对现存的 String 值创建新拷贝,并对新拷贝进行传递或赋值操作。值类型在 结构体和枚举是值类型 中有详细描述。

Swift 默认 String 拷贝的行为是为了保证在函数或方法中传递的是 String 值,不管该值是从哪里来,你都绝对拥有这个 String  值。你可以确定你传递的这个字符串不会被修改,除非你自己去修改它。

另一方面,Swift 编译器优化了字符串的使用,实际拷贝只会在需要的时候才进行。这意味着你把字符串当做值类型的同时也能够得到很棒的性能。


使用字符

你可以使用 for-in 循环来遍历 String 中每个的 Character 的值:

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

for-in 循环在 For-In 循环 有详细描述。

另外,你可以使用 Character 类型声明,并赋值一个单字符值创建一个独立的字符常量或变量:

let exclamationMark: Character = "!"

String 的值可以使用一个 Character 值类型的数组作为变量来进行初始化:

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


字符串和字符的拼接

可以使用加号( + )将 String 的值加(或 拼接 )在一起创造出一个新的值:

let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// 现在 welcome 的值等同于 "hello there"

你可以使用加等于赋值符号( += )将一个 String 的值追加到一个已经存在的 String 变量中:

var instruction = "look over"
instruction += string2
// 现在 instruction 的值等于 "look over there"

你可以使用 Stringappend() 方法将一个 Character 的值追加到一个 String 变量中:

let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// 现在 welcome 的值是 "hello there!"

注意

你不能将字符串 String 或字符 Character 拼接到 Character 变量中,因为 Character 的值只能包含单个字符。

如果你使用多行字符串文字构建一个更长的字符串,你希望每一行的结尾都有一个换行,包括最后一行,例如:

let badStart = """
one
two
"""
let end = """
three
"""
print(badStart + end)
// 打印两行:
// one
// twothree

let goodStart = """
one
two

"""
print(goodStart + end)
// 打印三行:
// one
// two
// three

上面的代码中 badStart 拼接上 end 得到是一个两行字符串,这并不是想要的结果。因为 badStart 的最后一行没有换行,该行直接连上了 end 的第一行。与此相反的是,goodStart 的两行都有换行,因此当它和 end 进行组合之后就能得到三行结果。


字符串插值

字符串插值 是一种从混合常量、变量、字面量和表达式的字符串字面量构造新的 String  值的方法。你可以在单行和多行字符串文本中使用字符串插值。每一个你插入字符串字面量的元素都要被一对圆括号包裹,然后使用反斜杠 (\) 前缀:

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 的真实值代替。

multiplier 的值同时也是字符串后边更大一点表达式的一部分。这个表达式计算了 Double(multiplier) * 2.5 的值并且插入结果 ( 7.5 ) 到字符串中。在这个例子中,表达式在字符串字面量中被写作  \(Double(multiplier) * 2.5) 的形式 。

注意

你作为插值写在圆括号中的表达式不能包含反斜杠 ( \ ),回车符或者换行符。总而言之,它们可以包含其他字符串字面量。


Unicode

Unicode 是一个在不同书写系统中编码,表示和处理文本的国际标准。它使你能够以一种标准化的形式表示几乎任何语言中的任何字符,并从外部源(比如文本或网页)中读取和写入这些字符。如本节所述,Swift 中的 StringCharacter 完全符合 Unicode 标准的。

Unicode 标量

在底层,Swift 中的原生 String 类型是由 Unicode 标量 构造而来的。Unicode 标量是一个独一无二的 21 位数组,对应一个字符或者修饰符,比如 U+0061 对应 LATIN SMALL LETTER A ("a"), U+1F425 对应 FRONT-FACING BABY CHICK ("🐥")。

注意

Unicode Scalar 是 U+0000U+E000U+E000 到 U+10FFFF 之间的任何 Unicode 码点,但不包括从  U+D800 到 U+DFFF 之间的代理对码点。

注意,并不是所有的 21 位 Unicode 标量都被分配给了一个字符——一些标量是被保留用于将来分配。已分配给字符的标量通常都有一个名字,比如上边提到的 LATIN SMALL LETTER A 和 FRONT-FACING BABY CHICK

可扩展的字形群集

每一个 Swift 的 Character 类型代表一个 可扩展 的字形群。 一个可扩展的字形群是一个或多个可生成人类可读的字符 Unicode 标量的有序排列。

举个例子,字母 é 可以用单一的 Unicode 标量 é (LATIN SMALL LETTER E WITH ACUTE, 或者 U+00E9)来表示。然而一个标准的字母 e (LATIN SMALL LETTER E 或者U+0065) 加上一个急促重音(COMBINING ACTUE ACCENT)的标量(U+0301),这样一对标量就表示了同样的字母 é 。 这个急促重音的标量形象的将 e 转换成了  é 。

在这两种情况中,字母 é 代表了一个单一的 Swift 的 Character 值,同时代表了一个可扩展的字形群。 在第一种情况,这个字形群包含一个单一标量;而在第二种情况,它是包含两个标量的字形群:

let eAcute: Character = "\u{E9}"                         // é
let combinedEAcute: Character = "\u{65}\u{301}"          // e 后面加上 ́
// eAcute 是 é, combinedEAcute 是 é

可扩展的字符群集是一个灵活的方法,用许多复杂的脚本字符表示单一的 Character 值。 例如,来自朝鲜语字母表的韩语音节能表示为组合或分解的有序排列。 在 Swift 都会表示为同一个单一的 Character 值:

let precomposed: Character = "\u{D55C}"                  // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"   // ᄒ, ᅡ, ᆫ
// precomposed 是 한, decomposed 是 한

可拓展的字符群集可以使包围记号(例如 COMBINING ENCLOSING CIRCLE, 或者 U+20DD)的标量包围其他 Unicode 标量,作为一个单一的 Character 值:

let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute 是 é⃝

地域性指示符号的 Unicode 标量可以组合成一个单一的 Character 值,例如 REGIONAL INDICATOR SYMBOL LETTER U (U+1F1FA)和  REGIONAL INDICATOR SYMBOL LETTER S (U+1F1F8):

let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS 是 🇺🇸


字符计数

在一个字符串中使用 count 属性去计算 Character 类型值个数:

let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
print("unusualMenagerie has \(unusualMenagerie.count) characters")
// 打印 "unusualMenagerie has 40 characters"

注意,Swift 对 Character 类型值使用了拓展字母集,意味着字符串的拼接和修改不一定会持续影响字符串字符个数。

例如,你初始化一个拥有四个字符的字符串 cafe,然后再追加一个 COMBINING ACUTE ACCENT (U+0301) 字符在末尾 ,最终形成的字符串还是拥有四个字符,并且最后一个字符是 ,而不是 e

var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// 打印 "the number of characters in cafe is 4"

word += "\u{301}"    // 拼接重音符,U+0301

print("the number of characters in \(word) is \(word.count)")
// 打印 "the number of characters in café is 4"

注意

拓展字母集可以由多个不同的 Unicode 标量组成,这就意味着相同字符和相同字符的不同表示需要占据不同的内存空间去存储,因此,在字符串的各种表示中 Swift 字符占据的内存并不一样。造成的结果就是,字符串的字符数量并不能通过遍历该字符串去计算,并用于确定该字符串的拓展字符集边界。如果你正在处理特别长的字符串,要意识到为了确定该字符串的字符个数, count 属性必须要遍历完整个字符串中的全部 Unicode 标量。

 count 属性返回的字符个数不会一直都与包含相同字符的 NSString 的 length 属性返回的字符个数相同。 NSString 的长度是基于 UTF-16 表示的字符串所占据的 16 位代码单元的个数决定,而不是字符串中的拓展字母集个数决定。


访问和修改字符串

你可以通过字符串的方法和属性来访问和修改它,或者通过下标语法。

字符串索引

每个 String 值都有一个关联的 索引类型, String.Index,对应着字符串中每个 Character 的位置。

正如上面提到的,不同的字符可能需要不同大小的内存存储,所以为了确定每个 Character 的具体位置,你必须从 String 的开头遍历每个 Unicode 标量到结束。因此,Swift 字符串不能使用整型值索引。

使用 startIndex 属性可以访问 String 的第一个 Character 的位置。使用 endIndex 属性可以访问 String 的最后一个 Character 的位置。因此, endIndex 属性并不是字符串下标的有效参数。如果 String 是空串, startIndex 和 endIndex 就是相等的。

你可以通过使用 String 的 index(before:) 和 index(after:) 方法,访问给定索引的前一个和后一个索引。要访问离给定索引偏移较多的索引,你可以使用 index(_:offsetBy:) 方法,避免多次调用 index(before:) and index(after:) 方法。

你可以使用下标语法来访问 String 特定索引的 Character 。

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

试图获取越界索引对应的 Character ,将引发一个运行时错误。

greeting[greeting.endIndex] // Error
greeting.index(after: greeting.endIndex) // Error

使用 indices 属性会创建一个包含全部索引的范围,用来在一个字符串中访问单个字符。

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

注意

你可以在任意一个遵循  Collection 协议的类型里面,使用 startIndex 和 endIndex 属性或者 index(before:)index(after:) 和 index(_:offsetBy:) 方法。如上文所示是使用在  String 中,你也可以使用在 ArrayDictionary 和 Set 中。

插入和删除

在一个字符串指定位置插入单个字符,使用 insert(:at:) 方法,而要插入另一个字符串的内容时,使用 insert(contentsOf:at:) 方法。

var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome 现在等于 "hello!"

welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
// welcome 现在等于 "hello there!"

删除一个字符串指定位置的单个字符,用 remove(at:) 方法,而要删除指定范围的子字符串时,用 removeSubrange(_:)

welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome 现在等于 "hello there"

let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome 现在等于 "hello"

注意

你可以在任何遵循 RangeReplaceableCollection 协议的类型上使用  insert(_:at:), insert(contentsOf:at:)remove(at:),和 removeSubrange(_:) 方法。除了这里说到的 String,还包括 ArrayDictionary,和  Set 等集合类型。


子字符串

当你从字符串中获取一个子字符串 —— 例如使用下标或者  prefix(_:) 之类的方法 —— 就可以得到一个 Substring实例 ,而非另外一个 String 。Swift 里的 Substring 的绝大部分函数都跟 String 一样,意味着你可以使用同样的方式去操作 Substring 和 String 。然而,跟  String 不同的是,你只有在短时间内需要操作字符串时,才会使用 Substring 。当你需要长时间保存结果时,就把 Substring 转化为 String 的实例:

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

// 把结果转化为 String 以便长期存储。
let newString = String(beginning)

就像 String ,每一个 Substring 都会在内存里保存字符集。而 String 和 SubString 的区别在于性能优化上,Substring 可以重用原 String 的内存空间,或者另一个 Substring 的内存空间(String 也有同样的优化,但如果两个 String 共享内存的话,它们就会相等)。这一优化意味着你在修改 String 和 Substring 之前都不需要消耗性能在内存复制。就像前面说的那样,Substring 不适合长期存储 —— 因为它重用了原 String 的内存空间,原 String 的内存空间必须保留直到它的 Substring 不再被使用为止。

上面的例子, greeting 是一个 String,意味着它在内存里有一片空间保存字符集。而由于 beginning 是 greeting 的 Substring,它重用了  greeting 的内存空间。相反,newString 是一个 String —— 它是使用 Substring 创建的,拥有一片自己的内存空间。下面的图展示了他们之间的关系:

0

注意

 String 和 Substring 都遵循  StringProtocol协议,  这意味着操作字符串的函数使用 StringProtocol 会更加方便。你可以传入 String 或 Substring 去调用函数。


比较字符串

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

如果他们的扩展字形集是 统一码等价,则这两个 String 值 (或者两个 Character 值) 被认为是等同的。如果它们具有相同的语言含义和外观,即使它们是由不同语义的 Unicode 标量组成,扩展字形集也是等同的。

例如,LATIN SMALL LETTER E WITH ACUTE (U+00E9) 在规范上等同于 LATIN SMALL LETTER E (U+0065) 加上 COMBINING ACUTE ACCENT (U+0301)。这两个扩展字形簇都是表示字符 é 的有效方法,因此它们被认为是规范等价的:

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

// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E 和 COMBINING ACUTE ACCENT
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"

if eAcuteQuestion == combinedEAcuteQuestion {
    print("These two strings are considered equal")
}
// 打印 "These two strings are considered equal"

相反,英文中的 LATIN CAPITAL LETTER A (U+0041,或 「A」),和俄文中的 CYRILLIC CAPITAL LETTER A (U+0410, 或 「А」) 不相等。这两个字符在视觉上相似,但具有不同的语言含义:

let latinCapitalLetterA: Character = "\u{41}"

let cyrillicCapitalLetterA: Character = "\u{0410}"

if latinCapitalLetterA != cyrillicCapitalLetterA {
    print("These two characters are not equivalent.")
}
// 打印 "These two characters are not equivalent."

注意

Swift 中的字符串和字符比较不是区域敏感的。

前缀和后缀比较

可以使用字符串的 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")
// 打印 "There are 5 scenes in Act 1"

同理,使用 hasSuffix(_:) 方法统计 Capulet's mansion 和 Friar Lawrence's cell 场景的出现次数:

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")
// 打印 "6 mansion scenes; 2 cell scenes"

注意

 hasPrefix(_:) 和 hasSuffix(_:) 方法都是在每个字符串的扩展字符集中逐个字符进行比较, 如本文所述  字符串和字符的比较


字符串的 Unicode 表示形式

当一个 Unicode 字符串被写入文本文件或者一些其他存储时,字符串中的 Unicode 标量会用 Unicode 定义的几种 编码格式 编码。每一个字符串中的小块编码都叫做 代码单元。这些包括 UTF-8 编码格式 (编码字符串为 8 位的代码单元),UTF-16 编码格式 (编码字符串为16位的代码单元) , 以及 UTF-32 编码格式 (编码字符串32位的代码单元) 。

Swift 提供几种不同的方式来访问字符串的 Unicode 表现形式。 你可以使用 for - in 对字符串进行便利, 进而访问其中单个 Character 字符值作为 Unicode 扩展的字符群集。 这个过程描述在 使用字符

另外,也可以通过其他三种 Unicode 兼容的方式访问字符串的值:

  • UTF-8 代码单元集合(利用字符串的 utf8 属性进行访问)
  • UTF-16 代码单元集合 (利用字符串的 utf16 属性进行访问)
  • 21 位的 Unicode 标量值集合,也就是字符串的 UTF-32 编码格式(利用字符串的 unicodeScalars 属性进行访问)

下面有 Dog , !!DOUBLE EXCLAMATION MARK ,或Unicode 标量 U+203C )和 🐶DOG FACE,Unicode 标量为 U+1F436)组成的字符串中的每一个字符代表着一种不同的表示:

let dogString = "Dog‼🐶"

UTF-8 表示形式

你可以通过遍历 Stringutf8 属性来访问他的 UTF-8 表示。这个属性是 string.UTF8View 类型的,UTF8View 是无符号 8 位( UInt8 )值得集合,每一个字节都对应一个字符串的 UTF-8 的表现形式:

0

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

上面的例子中,前三个 10 进制 codeUnit 值(68111103)代表了字符 D og ,他们的 UTF-8 表示和 ASCII 表示相同。接下来的三个 10 进制 codeUnit 值(226128, 188)是 DOUBLE EXCLAMATION MARK 的 3 字节 UTF-8 表示形式。 最后四个 codeUnit 值 (240159144182) 是 DOG FACE 的 4 字节 UTF-8 表示形式。

UTF-16 表示形式

你可以通过遍历 Stringutf16 属性来访问它的 UTF-16 表示形式。它是 String.UTF16View 类型的属性, 它是一个无符号 16 位 (UInt16) 值的集合,每一个 UInt16 都是一个字符的 UTF-16 表示形式:

0

for codeUnit in dogString.utf16 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// 打印 "68 111 103 8252 55357 56374 "

同样,前三个 codeUnit 值 (68111103) 代表了字符 Do, 和 g, 他们的 UTF-16 代码单元和 UTF-8 完全相同 (因为这些 Unicode 标量表示 ASCII 字符)。

第四个 codeUnit 值 (8252) 是一个等于十六进制 203C 的十进制值,
这代表了 DOUBLE EXCLAMATION MARK 字符的 Unicode 标量值 U+203C 。这个字符在 UTF-16 中可以用一个代码单元表示。

第五个和第六个 codeUnit 值 (55357 和 56374) 是 DOG FACE 字符的 UTF-16 表示形式。 第一个值为 U+D83D (十进制值为 55357 ) 第二个值为 U+DC36 (十进制值为 56374 )。

Unicode 标量表示形式

你可以通过遍历 String 值的 unicodeScalars 属性来访问它的 Unicode 标量表示。 它是一个 UnicodeScalarView 类型的属性, UnicodeScalarView 是 UnicodeScalar 类型的值得集合。

每一个 UnicodeScalar都有一个 value属性,可以返回对应的 21 位数值,用 UInt32 值来表示:

0

for scalar in dogString.unicodeScalars {
    print("\(scalar.value) ", terminator: "")
}
print("")
// 打印 "68 111 103 8252 128054 "

前三个 UnicodeScalar 值 (68111103) 的 Value 属性依旧代表着字符 Do, and g

第四个 codeUnit 值 (8252) 依旧是一个等于十六进制 203C 的十进制值, 这代表了 DOUBLE EXCLAMATION MARK字符的 Unicode 标量 U+203C

第五个 UnicodeScalar 值的 Value 属性, 128054, 是一个十六进制 1F436 的十进制表现, 它代表 DOG FACE 字符的 Unicode 标量 U+1F436

作为查询他们的 value 属性的一种替代方法, 每一个 UnicodeScalar 值也可以用来构建一个新的 String 值, 比如在字符串插值中使用:

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

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/the-swift-progr...

译文地址:https://learnku.com/docs/the-swift-progr...

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~