为什么拆分带有重音的字符串会崩溃?

时间:2016-12-07 19:44:26

标签: swift split foundation

这应该很简单......

import Foundation

let str:String = "Beyonce\u{301} and Tay"
print(str)

print(str.components(separatedBy: CharacterSet(charactersIn: "e")))

编译很好,直到我运行可执行文件:

// Beyoncé and Tay
// Illegal instruction (core dumped)

我怀疑Swift很难处理组合'\u{65}'重音标记,但考虑到语言强调其基于字形的字符串模型,我认为分裂"Beyonce\u{301} and Tay"会非常清楚在'e'上应该只提供["B", "yonce\u{301} and Tay"],因为'e\u{301}'应该被解释为单个字素而不是'e'加上组合急性。

分割单个字符不会崩溃:

print(str.components(separatedBy: "e"))
// ["B", "yoncé and Tay"]

我的快速版本是

swiftc -version
Swift version 3.0-dev (LLVM 3e3d712024, Clang 09ad59b006, Swift fdf6ee20e4)
Target: x86_64-unknown-linux-gnu

1 个答案:

答案 0 :(得分:1)

看起来Swift的Linux端口中存在一个错误。我不会在答案中说明这一点。以下代码在Mac OS X上进行了测试。

您遇到了Unicode的规范化问题。字母é可以用两种方式表达,Swift认为相同:

let s1 = "e\u{301}" // letter e + combining acute accent
let s2 = "\u{0e9}"  // small letter e with acute

s1.characters.count // 1
s2.characters.count // 1
s1 == s2            // true

这是因为Swift的String与其前身NSString一样,对Unicode非常有支持。但如果你深入研究,你会发现一些差异:

s1.utf16.count // 2
s2.utf16.count // 1

因此,即使s1s2相等,它们的存储方式也不同:使用2个或1个代码点。 components(seperatedBy: )对这一事实视而不见。它迭代字符串中的所有代码点,如果找到字母e则拆分。在一种形式之间转换为另一种形式称为规范化并影响该函数的工作方式:

let str1 = "Beyonce\u{301} and Tay"
let str2 = str1.precomposedStringWithCanonicalMapping // normalize the string to Form C

let charset = CharacterSet(charactersIn: "e")
str1.components(separatedBy: charset) // ["B", "yonc", "́ and Tay"]
str2.components(separatedBy: charset) // ["B", "yoncé and Tay"]

参考文献: