在 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

相關文章
相關標籤/搜索