作者:Ole Begemann,原文鏈接,原文日期:2016-01-03
譯者:aaaron7;校對:numbbbbb;定稿:Cee
ICU 的字符串變換很酷。ICU 庫提供了一整套強(qiáng)大的文本變換功能,在處理用戶輸入、特別是當(dāng)你的程序需要處理一些英語之外的語言或者非拉丁字符時非常有用。舉個例子,你可以把一段簡體中文轉(zhuǎn)碼成拉丁字符,同時清除音調(diào)符號、修飾符和隱藏字符,最后全部轉(zhuǎn)換成小寫,使其成為可以被你的數(shù)據(jù)庫搜索 API 識別的字符串,而所有這些變換,只要一行代碼就可完成。
在 Apple 的平臺中,字符串變換一直以來都是通過 Core Foundation 的 CFStringTranform 函數(shù)來實現(xiàn)。Mattt Thompson 在 NSHipster 上對該 API 有非常棒的介紹,推薦閱讀。
隨著 iOS 9 和 OS X 10.11 的發(fā)布,字符串變換被整合到了 Foundation 框架中。雖然在文檔中還沒有介紹 NSString 的新方法 stringByApplyingTransform(_:reverse:),但是 CFStringTransform 文檔已經(jīng)對它進(jìn)行了說明,而且 Nate Cook 在 這篇 NSHipster 的文章中也展示了一些具體的例子。下面的代碼演示了如何實現(xiàn)中文到拉丁字符的轉(zhuǎn)換:
import Foundation
let shanghai = "上海"
shanghai.stringByApplyingTransform(NSStringTransformToLatin,
reverse: false) // 返回 "shàng hǎi"
看起來還不錯。Apple 提供了 16 種固定的變換,絕大多數(shù)都是字符轉(zhuǎn)碼(譯者注:Script Transliterations,指的是把其中一種語言的字符變換成另一種語言的字符。詳情可參考這里),其中一些方法允許你清除輸入字符的組合標(biāo)記符號和讀音符號、轉(zhuǎn)換為碼點(diǎn)以及轉(zhuǎn)換為標(biāo)準(zhǔn)的 Unicode 形式。另外,絕大多數(shù)變換都是可逆的,只要設(shè)置 stringByApplyingTransform(_:reverse:) 函數(shù)的第二個參數(shù)即可。特別是做鏈?zhǔn)秸{(diào)用變換操作的時候,這顯得非常強(qiáng)大(比如首先轉(zhuǎn)碼,然后去除變音符號)。
自由變換
有一個牛逼功能,CFStringTransform 文檔和 NSHipster 的文章中都有提到,但我之前一直沒意識到,它就是自由變換。 ICU 自己定義了一套語法來表示變換,如果你把遵循這套語法的字符串作為參數(shù)傳給 stringByApplyingTransform(_:reverse:) ,它就可以識別!比如這樣:
// 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 用戶手冊寫的非常好,并且包含很多例子。強(qiáng)烈推薦你學(xué)習(xí)一下。這里是幾個我做的例子:
轉(zhuǎn)換成小寫。
| 輸入 | 變換 | 結(jié)果 |
|---|---|---|
| HELLO WORLD | Lower | hello world |
僅轉(zhuǎn)換元音字母為小寫。 方括號定義了一個過濾器,表示只對滿足過濾條件的字符應(yīng)用變換規(guī)則。
| 輸入 | 變換 | 結(jié)果 |
|---|---|---|
| HELLO WORLD | [AEIOU] Lower | HeLLo WoRLD |
先轉(zhuǎn)成拉丁,再轉(zhuǎn)成 ASCII,最后轉(zhuǎn)換成小寫。 用分號把不同的轉(zhuǎn)換規(guī)則隔開。拉丁到 ASCII 這一步會移除變音符以及會把 ASCII 碼范圍之外的字符和標(biāo)點(diǎn)符號轉(zhuǎn)換成 ASCII 中與之最為接近的版本。
| 輸入 | 變換 | 結(jié)果 |
|---|---|---|
| 上海 | 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" |
刪除標(biāo)點(diǎn)。 刪除規(guī)則非常強(qiáng)大。上面的例子都是用方括號加一些字符串規(guī)則來表示過濾條件,但過濾器也可以像這個例子一樣,由 Unicode 字符類給出。
| 輸入 | 變換 | 結(jié)果 |
|---|---|---|
| “Make it so,” said Picard. | [:Punctuation:] Remove | Make it so said Picard |
刪除所有非字母字符。使用 ^ 來對字符串做過濾。
| 輸入 | 變換 | 結(jié)果 |
|---|---|---|
| 5 plus 6 equals 11 ??! | [:^Letter:] Remove | plusequals |
把標(biāo)點(diǎn)符號轉(zhuǎn)換成印刷體。Publishing 規(guī)則可以直接把標(biāo)點(diǎn)符號轉(zhuǎn)換成對應(yīng)的印刷版本。
| 輸入 | 變換 | 結(jié)果 |
|---|---|---|
| "How's it going?" | Publishing | “How’s it going?” |
轉(zhuǎn)換為十六進(jìn)制表示法。支持很多種格式。默認(rèn)是 Java 格式。需要注意的是,這里 Java 輸出的是 UTF-16 字符單元(表情分為兩部分編碼),而其他格式則是輸出碼點(diǎn)。
| 輸入 | 變換 | 結(jié)果 |
|---|---|---|
| ??! | Hex | \uD83D\uDE03\u0021 |
| ??! | Hex/Java | \uD83D\uDE03\u0021 |
| ??! | Hex/Unicode | U+1F603U+0021 |
| ??! | Hex/Perl | \x{1F603} \x{21} |
| ??! | Hex/XML | ??! |
轉(zhuǎn)換成多種標(biāo)準(zhǔn)化的形式。
| 輸入 | 變換 | 結(jié)果 |
|---|---|---|
| é | NFD; Hex/Unicode | U+0065U+0301 |
| é | NFC; Hex/Unicode | U+00E9 |
| 2? | NFKD | 28 |
| 2? | NFKC | 28 |
想象一下,自己實現(xiàn)上述轉(zhuǎn)換方法多么蛋疼……
自由變換的知識我是從 Florian 和 Daniel 寫的那本 Core Data 里學(xué)來的。他們介紹了如何把用戶輸入的搜索詞標(biāo)準(zhǔn)化后再提交到數(shù)據(jù)庫。 這樣既可以有效提升搜索性能,也能讓搜索的結(jié)果更加準(zhǔn)確。
本文由 SwiftGG 翻譯組翻譯,已經(jīng)獲得作者翻譯授權(quán),最新文章請訪問 http://swift.gg。