Strings and Characters 字符串与字符

660 阅读23分钟

一个字符串是字符的序列,例如”hello,world”或者“albatross”。swift字符串由String类型表示。String的内容可以以克重形式进行操作,包括当成一个字符值的序列。

swift的String和Character类型提供一个快速,Unicode编码的方式来操作代码中的文本。字符串的创建和操作的语法简单并且可读性好,使用和C相似的字符串字面量语法。字符串联合像使用+操作符将两个字符串结合在一起一样简单,就像swift中其他的值一样。你也可以使用字符串来将常量,变量,字面量和表达式插入更长的字符串里面,字符串插入的过程中。这使得创建自定义字符串来展示,存储,打印变得很简单。

尽管语法简单,swift的字符串类型是一个快速,现代的字符串实现。每一个字符串由独立编码的Unicode字符组成,并且支持以各种Unicode形式操作这些字符。

swift的String类型和Foundation的NSString类桥接。foundation也扩展了String来支持定义在NSString中的方法。这意味着无需切换就可以在String上使用NSString方法。更多关于通过Foundation和Cocoa使用String的信息,查看Bridging Between String and NSString.

字符串字面量(String Literals)

可以在你的代码中用字面量字符串预定义String的值。字符串字面量是由双引号(“)包住的义序列的字符。

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

let someString = "Some string literal value"

记住,swift推导出sonmeString常量的类型是String,因为它是用一个字符串字面量值初始化的。

多行字符串字面量(Multiline String Literals)

如果你需要跨多行的字符串,使用多行字符串字面量--一个由三个双引号包住的字符序列:

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其他行前要忽略的空格。不过,如果你在行首多写了除闭双引号之外的空格,这些空格就会包含进去。

../_images/multilineStringWhitespace_2x.png

在上面的例子中,即使整个多行字面量字符串,第一行和最后一行没有以任何空格开始。中间行比闭双引号有更多的缩进,所以它以额外四个空格缩进开头。

字符串字面量中特殊符号

字面量字符串可以包含下列特殊字符:

  • 转义特殊字符\0(null字符),\\(反斜线),\t(水平制表符),\n(换行),\r(返回),\"(双引号),和\'(单引号)
  • 一个二进制Unicode编码的值,\u{n},n是一个1~8位16进制的数字(Unicode的描述在下面 Unicode中)

下面代码展示了这种特殊字符的例子。wiseWords常量包含两个转义double双引号标记。dollarSign,blackHeart,sparklingHeart常量展示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

因为多行字符串字面量使用三双引号标记而不是只有一个,可以在多行字符串字面量中无需转义包含一个双引号。在多行字符串中包含一个”“”,至少转义一个双引号。如下:

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

扩展字符串分隔符(Extended String Delimiters)

可以将字面量字符放在扩展分隔符中来不产生作用地把它们加入到字符串中。将字符串放在双引号中并且使用#包住它。例如,打印字面量字符串#“Line 1\nLine 2"#打印的是换行符(\n)而不是用两行吧字符串打印出来。

如果需要字面量字符串中字符的特殊功能,匹配跟着转义符的字符串中#的数个数。例如,如果你的字符串是#Line 1\nLine 2“#并且你想换行,你可以使用#”Line 1\#nLine 2"。相似地,###"Line\###nLine2"###也换行了。

字符串字面量用扩展分隔符创建也可以是多行字面量字符串。可以使用扩展分隔符来在多行字符串中包含“”“,重写字面量结尾默认的形式。例如:

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

初始化空字符串(Initializing an Empty String)

创建一个空String值像开始写长字符串一样,给变量分配一个空字符串字面量,或者使用初始化语法初始化一个新的String实例:

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

通过检查它的布尔值isEmpty属性确定一个字符串的值是不是空的:

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

可变字符串(String Mutability)

通过将它分配成一个变量(这种情况下可以改变)指定一个特定的String可以改变的,或者分配成一个常量(这种情况下不可以修改):

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中可变字符串不一样,Objective-C中在两个类(NSString和NSMutableString)中选择来确定一个字符串是否可以改变。

字符串是值类型(Strings Are Value Types)

swift的String类型是一个值类型。如果你创建了一个新的String类型的值,String类型的值会在它传给函数或者方法,或者把它分配给常量或者变量时进行复制。每种情况,都创建已存在的String的值的新复制,传递分配新的复制,不是原先的版本。值类型的描述在Structures and Enumerations Are Value Types.

swift的默认复制String的表现确保了当函数或者方法传给你一个String值时,很明确的是你拥有这个String值,不管它从哪里来。你可以十分确定传给你的字符串除了你改变它外不会被修改。

背后,swift的编译器 优化了字符串的使用,当确实必要的时候真正的复制才会发生。这意味着当你把字符串当成值类型时会有很不错的表现性能。

使用字符(Working with Characters)

你可以通过使用for-in循环遍历字符串来操作一个字符串单独的字符:

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

for-in循环的描述在For-In Loops.

或者,通过提供一个Character类型声明使用一个单字符字符串字面量来创建一个单独的Chanracter常量或者变量:

let exclamationMark: Character = "!"

String值可以通过传递Character类型的数组作为初始化参数的来创建:

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

你也可以使用加法分配符给已经存在的String变量拼接一个String值:

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

你也可以使用String类型的append()方法给String变量拼接一个Character值:

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

不能给Character变量拼接一个String或者Character,因为一个Character值只能包含一个Character。

如果你使用多行字符串字面量创建多行的长字符串,想要字符串的每一行以换行符结尾,包括最后一行。例如:

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拼接在一起生成一个两行的字符串,并不是期望的结果。因为badStart的最后一行不是以换行符结尾的,那一行与end的第一行结合在一起。相比较起来,goodStart的两行是以换行符结尾的,所以当他与end结合时结果有三行,像期望的那样。

字符串插入(String Interpolation)

字符串插入是由混合的常量,变量,字面量和表达式并把它们包含在一个字符串字面量中来构建一个新的字符串的方式。在单行和多行字符串字面量中都可以使用字符串插入。插入字符串字面量中的每一项包在一对括号中,前置一个反斜线:

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)。

可以使用扩展字符串分隔符来创建包含不被当做字符串插入的字符的字符串。例如:

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

在使用扩展分隔符的字符串中使用字符串插入,匹配反斜杠前#的个数与在字符串开头和结尾的#的个数。例如:

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

在内插字符串中写在括号中的表达式不能含有非转义的反斜线,回车,或者换行。不过可以包含其他字符串字面量。

Unicode

Unicode是在不同操作系统中的一个国际编码,表达和操作文本的标准。使你能够以一个标准的形式表达任何语言的任何字符,可以从像文本文件或者网页的外部资源中读取或者写入这些字符。swift的String和Character类型都是完全Unicode编译,像这一节中所说的。

Unicode标量值(Unicode Scalar Values)

背后,swift本地String类型是由Unicode标量值构建的。一个字符或者修饰语的Unicode标量值是一个独一无二的21位的数字,例如U+0061是LATIN SMALL LETTER A ("a"),orz和U+1F425是FRONT-FACING BABY CHICK ("🐥")。

记住不是全部21位Unicode表两只都分配了一个字符--一些标量值保留来为将来的分配或者为了使用UTF-16编码。已经分配了字符的下标值也有名字,例如上面的LATIN SMALL LETTER A和FRONT-FACING BABY CHICK。

扩展字母簇

swift的Character类型的每一个实例代表了一个单一的扩展字母簇。一个扩展的字母簇是一个或多个(结合时)可以制造一个单一的人类可读的字符的Unicode标量的序列。

这里是一个例子。字母é可以表示为单个Unicode标量é(LATIN SMALL LETTER E WITH ACUTE, or U+00E9)。不过,相同的字母也可以表示为一对下标量--一个标准的字母e (LATIN SMALL LETTER E, or U+0065),跟着COMBINING ACUTE ACCENT scalar (U+0301)。标量COMBINING ACUTE ACCENT图标形式用在前面的标量上,当它被Unicode-aware文本渲染系统渲染时从e变为了é。

两种情况下,字母é用表示一个扩展字母簇的swif的Character值来表示。在第一个情况中,簇包含一个单独的标量,第二个情况,它是两个标量的簇:

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

扩展字母簇是把许多复杂笔画的字母表示成一个简单Character值的灵活的方法。例如,韩文字母表中的Hangul字节可以表示为一个预先组合的或者分解的序列。两种表示方式在swift中都表示一个Character值:

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

扩展字母簇可以使包围的下标(像COMBINING ENCLOSING CIRCLE, or U+20DD)来包住其他Unicode标量作为一个单一的Character值:

let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute is 


地区符号的Unicode标量可以由一对组合来生成一个单独的Character值,像这个组合REGIONAL INDICATOR SYMBOL LETTER U (U+1F1FA) 和 REGIONAL INDICATOR SYMBOL LETTER S (U+1F1F8):

字符计数(Counting Characters)

要检索一个字符串中Character值的个数,使用字符串的count属性:

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

记住swift对Charact值使用扩展字母簇意味着字符串联系和修改可能影响一个字符串的字符个数。

例如,如果你使用四个字母的单词cafe初始化一个新的字符串,然后拼接上COMBINING ACUTE ACCENT (U+0301)来结束这个字符串,生成的字符串仍然有四个字母,第四个字母是而不是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 café is 4"

扩展字母簇可以由多个Unicode标量组成。这意味着不同的字母--和相同字母的不同表示--需要不同的数量的内存来储存。因此,swift中的字母在字符串中的表示都不占用一样数量的内存。导致字符串中字母的个数在没有遍历字符串判断它的扩展字母簇范围时计算不出来。如果你使用特别长的字符串值,注意count属性一定要遍历整个Unicode标量来决定这个字符串的字母。cout属性返回的字母个数通常跟含有相同字母的Nsstring的length属性不总是一样。NSString的长度是在字符串的UTF-16表示中16-位代码的个数的基础上而不是在字符串中Unicode扩展字母簇的数量。

操作和调整一个字符串(Accessing and Modifying a String)

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

字符串下标(String Indices)

每一个字符串的值有一个关联的index类型,String.Index,与字符串中每一个字母的位置一致。

像上面提到的,不同的字母可能需要不同的内存个数来储存,所以要想确定在特定位置上是哪一个Character,你要从字符串的开头到结束遍历每一个Unicode标量。因此,swift的字符串不能使用整型值做下标。

使用startIndex属性来访问一个字符串的第一个字母的位置。endIndex属性是字符串中最后一个字母后的位置。因此,endIndex属性对于字符串的下标不是一个有效的参数。如果字符串是空的,startIndex和endIndex事项等的。

可以使用String的Index(before:)和index(after:)方法访问一个给定下标之前和之后的下标。要访问远离给定下标的下标,可以使用index(_:offsetBy:)方法替代多次调用这些方法。

你可以使用下标语法来访问在特定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

尝试访问超过字符串区间的下标或者在字符串区间之外的下标的字母将会触发运行时错误。

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协议的类型使用startIndex和endIndex属性和index(before:),index(after:),和index(_:offsetBy:)方法。包括String,像这里展示的一样,也有collection类型像Array,Dictionary,和Set。

插入和移除(Inserting and Removing)

要在指定的位置向字符串中插入一个单独的字母,使用insert(_:at:)方法,要在特定的位置插入另一个字符串的内容,使用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:)方法,移除指定区域的一个子字符串,使用方法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协议的类型中使用欧冠insert(_:at:),inset(contentsOf:at:),remove(at:),和removeSubrange(_:)方法。这包括String,像这里展示的,也包括collection类型像Array,Dictionary,和Set。

子串(Substrings)

当你从字符串中获得一个子串时,--例如,使用下标或者方法,像prefix(_:)--结果是一个Substring的实例,不是另一个字符串。swift中的Substrings有和Strings一样的方法,意味着你可以以你知道的相同的使用strings的方法使用Substrings。不过,不像strings,当对字符串进行操作时你只能使用Substrings一小段时间。当你准备存储结果一长段时间时,将Substrings转换成一个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)

像字符串,每一个子串有一块存储组成子串的字母的区域的内存。在strings和Substrings之间的不同是,为了优化性能,子串可以重复使用用来存储原始字符串的内存,或者部分用来存储另一个字串的内存。(字符串有一个相似的优化,但是如果两个字符串共享内存,他们是相等的。)这个性能优化意味着知道你改变字符串或者子串之前,不用承担内存复制的性能消耗。如上所说,子串不适合长期存储--因为他们重用了原始字符串的内存,整个字符串需要在它的子串使用期间一直持有内存。

在上面的例子中,greeting是一个字符串,意味着他有一个组成字符串的字母储存的内存区间。因为beginning是greeting的子串,它重用了greeting使用的内存。相反,newString是一个字符串--当它通过子串创建的时,他有自己的内存。下面的图标展示了这些关系:


字符串和子串都遵守StringProtocol协议,意味着对字符串操作函数来说接受StringProtocol类型的值很方便。你可以用String或者Substring值调用这些函数。

对比字符串(Comparing Strings)

swift提供了三种对比文本值的方式:字符串和字母相等,前缀相等,结尾相等。

字符串和字母相等(String and Character Equality)

字符串和子母相等使用”相等于“运算符(==)和”不等于“运算符”(!=),像Comparison Operators中描述的:

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"

如果他们的扩展字母簇是canonically相等的那么两个字符串的值(或者两个字母的值)认为是相等的。如果扩展字母簇有相同语意和外观,那么他们按规定是相等的,即使他们背后由不同的Unicode标量组成。

例如,LATIN SMALL LETTER E WITH ACUTE (U+00E9)按规定和跟着COMBINING ACUTE ACCENT (U+0301)的LATIN SMALL LETTER E (U+0065)是按规定相等的。这两个扩展字母簇都是表示字母é有效的方式,所以他们可以认为是按规定相等:

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

// "Voulez-vous un café?" 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"

相反地,LATIN CAPITAL LETTER A (U+0041, or "A"),用在英语中,不等于CYRILLIC CAPITAL LETTER A (U+0410, or "А"),用于俄语中。两个字母看起来相似,但没有相同的语意:

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中对比不是区域敏感的。

前缀和后缀相等(Prefix and Suffix Equality)

检查一个字符是否含有指定的前缀或者后缀字符串,调用字符串的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"
]

你可以通过用rmomeoAndJuliet数组使用hasPrefix(_;)方法来算出第一幕中背景的数目:

var act1SceneCount = 0
for scene in romeoAndJuliet {
    if scene.hasPrefix("Act 1 ") {
        act1SceneCount += 1
    }
}

同样的,使用hasSuffix(_:)方法计算发生或者靠近Capulet‘s mansion和Friar Lawrence’s的场景的数目:

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(_:)方法在每个字符串中扩展字母簇之间进行了一个字母一个字母按规则的相等比较,像String and Character Equality中描述的。

字符串的Unicode表示

当一个Unicode字符串写到一个文本文件或者其他一些内存中时,字符串中的Unicode标量编码成多个Unicode-定义的编码形式其中的一种。每一种形式将字符串编码到称为code units的小块中。包括UTF-8编码形式(编码成一个8-bit单位的字符串),UTF-16编码形式(编码成16-bit单位的字符串),UTF-32编码形式(编码成一个32-bit单位的字符串)。

swift提供了多个不同的访问字符串的Unicode表示的方法。可以使用for-in语句遍历字符串,以Unicode扩展字母簇的方式访问他的单个的字母。这个过程描述在Working with Characters

或者,以其他三个Unicode编译表示方式志气的方式访问String的值:

  • A collection of UTF-8 code units (accessed with the string’s utf8 property)
  • A collection of UTF-16 code units (accessed with the string’s utf16 property)
  • A collection of 21-bit Unicode scalar values, equivalent to the string’s UTF-32 encoding form (accessed with the string’s unicodeScalars property)

下面的每一个例子展示了下面字符串不同的表达方式,由字母D,o个,!!(DOUBLE EXCLAMATION MARK, or Unicode scalar U+203C),和🐶 character (DOG FACE, or Unicode scalar U+1F436)组成:

let dogString = "Dog‼🐶"

UTF-8表示(UTF-8 Representation)

可以通过遍历他的utf8属性来访问字符串的UTF-8的表示。这个属性的类型是String。UTF8View,是一个无符号性UInt8类型值的序列,用来表示字符串的UTF-8表示中的每一个字节:


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

上面的例子中,前三个十进制的codeUnit值是(68,111,103)表示字母D,o,和g,他们的UTF-8的表示跟他们的ASCII表示一样。接下来三个十进制codeUnit值(226,128,188)是字母DOUBLE EXCLAMATION MARK的一个三字节UTF-8表示。最后的四个codeUnit值(240,159,144,182)是DOG FACE字母的四字节UTF-8表示。

UTF-16表示(UTF-16Representation)

你可以通过遍历字符串的utf16属性来访问字符串的UTF-16的表示。这个属性的类型是String。UTF16View,是无符号型的16位(UInt16)值的数组,用于字符串的UTF-16表示中每个16位单元:


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)是一个时间至等于16进制的值203C,是DOUBLE EXCLAMATION MARK 字符的Unicode标量U+203C。这个字符可以用一个单位的UTF-16来表示。

第五个和第六个codeUnit值(55357和56374)是一个表示DOG FACE字符的UTF-16代理对。这些值是U+D83D(十进制55357)的高代理值和一个U+DC36的低代理值(十进制值56374)。

Unicode标量表示(Unicode Scalar Representation)

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

每一个UnicodeScalar有一个值属性,返回标量的21-位值,使用UInt32值表示:


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)又是等于16进制203C的十进制,表示DOUBLE EXCLAMATION MARK字符的Unicode标量U+203C。

第五个和最后一个UnicodeScalar的value属性,是等于16进制1F436的十进制,表示DOG FACE字符的Unicode标量U+1F436。

另一种查询他们Value属性的方法,每一个UnicodeScalar值也可以用来构建一个新的String值,例如使用字符串插入:

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