在 Cocoa 中实现 ICU 文本变换

做者:Ole Begemann,原文连接,原文日期:2016-01-03
译者:aaaron7;校对:numbbbbb;定稿:Ceehtml

ICU 的字符串变换很酷。ICU 库提供了一整套强大的文本变换功能,在处理用户输入、特别是当你的程序须要处理一些英语以外的语言或者非拉丁字符时很是有用。举个例子,你能够把一段简体中文转码成拉丁字符,同时清除音调符号、修饰符和隐藏字符,最后所有转换成小写,使其成为能够被你的数据库搜索 API 识别的字符串,而全部这些变换,只要一行代码就可完成。ios

在 Apple 的平台中,字符串变换一直以来都是经过 Core Foundation 的 CFStringTranform 函数来实现。Mattt Thompson 在 NSHipster 上对该 API 有很是棒的介绍,推荐阅读。git

随着 iOS 9 和 OS X 10.11 的发布,字符串变换被整合到了 Foundation 框架中。虽然在文档中尚未介绍 NSString 的新方法 stringByApplyingTransform(_:reverse:),可是 CFStringTransform 文档已经对它进行了说明,并且 Nate Cook 在 这篇 NSHipster 的文章中也展现了一些具体的例子。下面的代码演示了如何实现中文到拉丁字符的转换:github

import Foundation
let shanghai = "上海"
shanghai.stringByApplyingTransform(NSStringTransformToLatin,
    reverse: false) // 返回 "shàng hǎi"

看起来还不错。Apple 提供了 16 种固定的变换,绝大多数都是字符转码(译者注:Script Transliterations,指的是把其中一种语言的字符变换成另外一种语言的字符。详情可参考这里),其中一些方法容许你清除输入字符的组合标记符号和读音符号、转换为码点以及转换为标准的 Unicode 形式。另外,绝大多数变换都是可逆的,只要设置 stringByApplyingTransform(_:reverse:) 函数的第二个参数便可。特别是作链式调用变换操做的时候,这显得很是强大(好比首先转码,而后去除变音符号)。数据库

自由变换

有一个牛逼功能,CFStringTransform 文档和 NSHipster 的文章中都有提到,但我以前一直没意识到,它就是自由变换。 ICU 本身定义了一套语法来表示变换,若是你把遵循这套语法的字符串做为参数传给 stringByApplyingTransform(_:reverse:) ,它就能够识别!好比这样:swift

// Convert non-ASCII characters to ASCII,
// convert to lowercase, delete spaces
"Café au lait".stringByApplyingTransform(
    "Latin-ASCII; Lower; [:Separator:] Remove;", reverse: false)
// returns "cafeaulait"

这篇 ICU 用户手册写的很是好,而且包含不少例子。强烈推荐你学习一下。这里是几个我作的例子:app

转换成小写。框架

输入 变换 结果
HELLO WORLD Lower hello world

仅转换元音字母为小写。 方括号定义了一个过滤器,表示只对知足过滤条件的字符应用变换规则。ide

输入 变换 结果
HELLO WORLD [AEIOU] Lower HeLLo WoRLD

先转成拉丁,再转成 ASCII,最后转换成小写。 用分号把不一样的转换规则隔开。拉丁到 ASCII 这一步会移除变音符以及会把 ASCII 码范围以外的字符和标点符号转换成 ASCII 中与之最为接近的版本。函数

输入 变换 结果
上海 Any-Latin; Latin-ASCII; Lower shang hai
København Any-Latin; Latin-ASCII; Lower kobenhavn
กรุงเทพมหานคร Any-Latin; Latin-ASCII; Lower krungthephmhankhr
Αθήνα Any-Latin; Latin-ASCII; Lower athena
“Æ « © 1984” Any-Latin; Latin-ASCII; Lower "ae << (c) 1984"

删除标点。 删除规则很是强大。上面的例子都是用方括号加一些字符串规则来表示过滤条件,但过滤器也能够像这个例子同样,由 Unicode 字符类给出。

输入 变换 结果
“Make it so,” said Picard. [:Punctuation:] Remove Make it so said Picard

删除全部非字母字符。使用 ^ 来对字符串作过滤。

输入 变换 结果
5 plus 6 equals 11 ?! [:^Letter:] Remove plusequals

把标点符号转换成印刷体。Publishing 规则能够直接把标点符号转换成对应的印刷版本。

输入 变换 结果
"How's it going?" Publishing “How’s it going?”

转换为十六进制表示法。支持不少种格式。默认是 Java 格式。须要注意的是,这里 Java 输出的是 UTF-16 字符单元(表情分为两部分编码),而其余格式则是输出码点。

输入 变换 结果
?! Hex \uD83D\uDE03\u0021
?! Hex/Java \uD83D\uDE03\u0021
?! Hex/Unicode U+1F603U+0021
?! Hex/Perl \x{1F603} \x{21}
?! Hex/XML &#x1F603;&#x21;

转换成多种标准化的形式。

输入 变换 结果
é NFD; Hex/Unicode U+0065U+0301
é NFC; Hex/Unicode U+00E9
2⁸ NFKD 28
2⁸ NFKC 28

想象一下,本身实现上述转换方法多么蛋疼……

自由变换的知识我是从 Florian 和 Daniel 写的那本 Core Data 里学来的。他们介绍了如何把用户输入的搜索词标准化后再提交到数据库。 这样既能够有效提高搜索性能,也能让搜索的结果更加准确。

本文由 SwiftGG 翻译组翻译,已经得到做者翻译受权,最新文章请访问 http://swift.gg

相关文章
相关标签/搜索