Swift字符串索引将"\ r \n"组合为一个char而不是两个

Fan*_*Lin 3 string swift swift4

我正在处理包含\r\nSwift 4.2的字符串.我遇到了一些Swift索引的奇怪行为,它看起来\r\n会被Swift索引方法视为一个字符而不是两个字符.我写了一段代码来表达这种行为:

var text = "ABC\r\n\r\nDEF"

func printChar(_ lower: Int, _ upper: Int) {
    let start = text.index(text.startIndex, offsetBy: lower)
    let end = text.index(text.startIndex, offsetBy: upper)
    print("\"" + text[start..<end] + "\"")
}

printChar(0, 1) // "A"
printChar(1, 2) // "B"
printChar(2, 3) // "C"
printChar(3, 4) // new line
printChar(4, 5) // new line (okay, what's going on here?)
printChar(5, 6) // "D"
printChar(6, 7) // "E"
printChar(7, 8) // "F"
Run Code Online (Sandbox Code Playgroud)

打印结果将是

"A"
"B"
"C"
"
"
"
"
"D"
"E"
"F"
Run Code Online (Sandbox Code Playgroud)

知道为什么会这样吗?

shi*_*him 8

TLDR:\r\n是一个字形集群,Character因为Unicode而在Swift中被视为单一集群.


  • 斯威夫特视为\r\n一体Character.

  • Objective-C NSString将其视为两个字符(根据结果length).

在swift-users论坛上有人写道:

- "\ r \n"是单曲Character.这是正确的行为吗?

- 是,a Character对应于Unicode字形集群,"\ r \n"被认为是单个字形集群.

随后的响应发布了一个指向Unicode文档的链接,请查看此表,该表正式表明CRLF是一个字形集群.

看一下关于Characters和Grapheme ClustersApple文档.

将字符串视为字符序列是很常见的,但是在使用NSString对象时,或者通常使用Unicode字符串时,在大多数情况下,最好处理子字符串而不是单个字符.其原因在于,在许多情况下,用户认为文本中的字符可以由字符串中的多个字符表示.

关于字符串和字符的Swift文档也值得一读.

objc.io的这个概述也很有趣.

NSString表示UTF-16编码的文本.长度,索引和范围均基于UTF-16代码单元.

另一个例子是表情符号.这个单个字符实际上是%uD83D%uDC4D%uD83C%uDFFB,四个不同的unicode标量.但是,如果你count用一个表情符号调用一个字符串,你(正确)得到1.

如果您想查看标量,可以按如下方式迭代它们:

for scalar in text.unicodeScalars {
    print("\(scalar.value) ", terminator: "")
}
Run Code Online (Sandbox Code Playgroud)

哪个"\r\n"会给你13 10

在Swift文档中,您将找到NSString不同的原因:

count属性返回的字符数不总是与包含相同字符的NSString的length属性相同.NSString的长度基于字符串的UTF-16表示中的16位代码单元的数量,而不是字符串中Unicode扩展的字形集群的数量.

因此,这不是Swift字符串索引的"奇怪"行为,而是Unicode如何处理这些字符以及如何String设计Swift的结果.Swift字符串索引经过Character而且\r\n是单一的Character.