Contents
  1. 1. Swift 2.0 中的字符串
    1. 1.1. 与字序列的总合不同
    2. 1.2. 根据字符内容进行判断
    3. 1.3. 取决于你的视角

Swift 2.0 中的字符串

原文链接:Strings in Swift 2

Swift 在它的标准库中提供了高效,Unicode 兼容的字符串实现。在 Swift 2.0 中,字符串不再遵守 CollectionType 协议,而在此之前,字符串被视为是一系列 Characher 类型值的集合,行为与数组十分类似。现在,字符串提供了一个 characters 属性,可以获得这个字符串所包含的 Character 集合。

为何要有这样的改变呢?尽管把字符串当作一系列字符的集合是十分自然的想法,然而字符串的行为与普通的集合类型,比如 Array, SetDictionary 是相当不一样的。虽然过去也存在这些的差异,但是随着 Swift 2.0 推出了协议扩展,使得我们有必要针对这些差异做出一些根本性的改变。

与字序列的总合不同

当向一个集合中添加一个元素的时候,我们期望这个集合将会包含这个元素。
也就是说,当向一个数组追回一个值的时候,这个数组将会包含这个值。这对集合(set)和字典也是适用的。然而,当我们向字符串追回一个组合标记字符(combing mark character)的时候,字符串的内容将会发生改变。

比如 cafe 这个字符串,它包含了 c, a, fe 这四个字符:

var letters: [Character] = ["c", "a", "f", "e"]
var string: String = String(letters)

print(letters.count) // 4
print(string) // cafe
print(string.characters.count) // 4

如果向字符串中追回一个组合重音字符 U+0301 ´,这个字符串还是包含四个字符,但是最后一个字符将会变为 é

let acuteAccent: Character = "\u{0301}" // ´ COMBINING ACUTE ACCENT' (U+0301)

string.append(acuteAccent)
print(string.characters.count) // 4
print(string.characters.last!) // é

该字符串的 characters 属性将不包含原来的小写字母 e,也不会包含刚刚追回的重音符号 ´。 它会将小写字母 “e” 添加重音符号,使其变成 é

string.characters.contains("e") // false
string.characters.contains("´") // false
string.characters.contains("é") // true

如果我们将字符串与其它的集合类型等同视之,我们就有可能得到向集合(set)中添加 UIColor.redColor()UIColor.greenColor() 却得到一个 UIColor.yellowColor() 的奇怪结果。

根据字符内容进行判断

字符串与其它集合类型的另一个差异就是判断两个值是否相等的标准。

  • 两个数组只有在他们包含的元素个数完全一致,并且每个位置上的元素都是相等的时候,才被视为相等。
  • 两个集合(set)只有在他们包含的元素个数完全一致,并且每个包含在第一个集合中的元素,也被第二个集合所包含时,才被视为相等。
  • 两个字典只有在他们包含相同的键值对时,才被视为相等。

然而,字符串是根据 规范等价(canonically equivalent) 来进行相等判断的。即使两个字符串的底层 Unicode 标量不一样,只要他们的语义跟表现形式是一致的,他们就被认为是相等的。

以韩语为例, 它们的手写形式包含了 24 个字母,或者称为 Jamo, 用以表达不同的元音和辅音。当书写的时候,这些字母可以组合来代表音节。比如,“가” ([ga]) 这个字符是由字母 “ᄀ” ([g]) 和 “ᅡ” [a] 组合而成的。在 Swift 中判断字符串是否相等的时候,将不管它们是否由其它字符组合而成,而只判断他们的语义与表达形式是否一致:

let decomposed = "\u{1100}\u{1161}" // ᄀ + ᅡ
let precomposed = "\u{AC00}" // 가

decomposed == precomposed // true

这个行为与 Swift 当中的其它集合类型又一次存在着重大的差异。试想,如果向一个数组当中添加 两个元素,最后却得到一个 ,想必写这段代码的程序员也会很惊讶吧。

取决于你的视角

字符串虽然不是集合类型。但是它提供了多个遵守 CollectionType 协议的属性,以提供不同的视角(views)

如果以前面示例中的单词 “café” 为例,将它分解成 [c, a, f, e] 和 [ ´ ] 的字符序列,下面以不同的视角来对这些字符序列进行表示:

café字符序列视角

  • characters 属性将文本分割为 扩展字形集群(extended grapheme clusters),即与用户所直接看到相一致的字符序列(在此处即为 c, a, f 和 é)。取得这个属性的时间复杂度是线性的 O(n), 因为字符串必须对整个字符串文本中的每一个位置(这里的位置被称为码位(code point))进行迭代以确定字符的边界。只要涉及到对人类可读(human-readable)文本,或者本地化(locale-sensitive)有关的 Unicode 算法时,比如 localizedStandardCompare(_:) 处理的字符串,或者 localizedLowercaseString 属性,都需要对字符串中的字符进行逐字的处理。
  • unicodeScalars 属性表示出了字符串底层所保存的标量值。如果原本的字符串是由预组合字符 é 而非分解的字符 e + ´ 所组成,则 é 会被以 Unicode标量的形式表示出来。当需要对字符串底层的字符数据进行处理的时候,我们可以使用这个 API。
  • utf8utf16 属性分别以 UTF-8 和 UTF-16 来对码位(code points)进行表示。这些数值与我们对一个特定的编码进行转换时,要真实写入文件的字节相对应。UTF-8 编码单元(code units)被许多 POSIX 的字符串处理 API 所使用,而 UTF-16 编码单元(code units)则被用于表示 Cocoa 和 Cocoa Touch中的字符串长度和偏移。

如果想了解更多 Swift 中关于的字符和字符串的信息,可以阅读The Swift Programming Languagethe Swift Standard Library Reference.

Contents
  1. 1. Swift 2.0 中的字符串
    1. 1.1. 与字序列的总合不同
    2. 1.2. 根据字符内容进行判断
    3. 1.3. 取决于你的视角