在 Mac OS X/iOS 上指定中文排序

前一陣子遇到一個需求:我們現在有一個 array,裡頭是一堆中文字串,現在我們想要讓這些中文字串按照一些個定的中文索引方式排序,像是使用筆劃、拼音…等等。搞了半天,發現只要在 compare 的時候,選擇特定的 NSLocale 就好了,但是蘋果自己的文件中,居然對於有這些 Locale 可以設沒什麼著墨,顯然很多時候光看蘋果的文件是不夠的。

我們先來看段使用筆畫排序的程式碼:

NSArray *a = @[
	@"落魄江湖載酒行",
	@"楚腰纖細掌中輕",
	@"十年一覺揚州夢",
	@"贏得青樓薄倖名"];
NSLocale *strokeSortingLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh@collation=stroke"];
a = [a sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
	return [obj1 compare:obj2 options:0 range:NSMakeRange(0, [obj1 length]) locale:strokeSortingLocale];
}];
for (NSString *s in a) {
	NSLog(@"%@", s);
}

在 Mac OS X/iOS 中,所有與各種不同國家、語言中使用的不同曆法、日期格式、數字格式相關的資訊,也就是 NSDateFormatter、NSNumberFormatter、NSLocale 這些 class 底下,其實呼叫的是 IBM 的 OpenSource 專案 ICU,所以呢,ICU 支援哪些 Locale,NSLocale 裡頭就可以使用哪些 Locale。

在 ICU 的 Locale 中,我們需要注意的,就是可以在語系(像是 zh)與地區(像是 TW)之後,還可以繼續加上 keyword,keyword 可以是曆法、排序方式、貨幣與數字格式,於是指定以筆畫順序排序的中文語系,就會寫成 zh@collation=stroke。ICU 還支援拼音(pinyin)、Big5 編碼順序(big5han)、GB2312 編碼順序(gb2312)以及部首筆畫順序(unihan)。

由於系統裡頭已經有了一份 ICU library,我們也可以直接呼叫 ICU 的 C 或是 C++ API,做我們在上面做的這件事情。但是在 Mac OS X/iOS 上面想要使用系統內建的 ICU library 還頂麻煩,雖然我們可以讓 linker 直接去連結 libicucore.dylib,但是系統裡頭卻沒有放 ICU 的 header,所以還是要去下載一份 ICU 把 header 挖出來用,另外還要設一些 compiler 參數(詳情可以參考 Lukhnos 去年的這篇 Using OS X’s Built-in ICU Library in Your Own Project),或,我還有一小段程式,大概示範了怎樣使用 ICU 的 C 與 C++ API 排序。但,既然這麼麻煩,我們還是直接呼叫 Cocoa Framework,寫 Objective C 就好了。

說到 ICU 就要講一下前幾年遇到的一個慘痛經驗。前幾年參與一個專案,看到蘋果當時在 Objective C 上面時做了 Garbage Collection,就高高興興拿來寫了個 Mac App,結果呢,蘋果做出來的 GC 真是一踏糊塗,在打開了 ObjC GC 的狀況下,如果不是在 main thread 呼叫架構在 ICU 上的 Foundation Class,像是前面提到的 NSLocale、NSDateFormatter,記憶體就開始狂洩不止,也難怪蘋果最後放棄了在 run time 做 GC 這條路,選擇了 ARC,在 compile time 幫你加上 retain、release,但 ARC 也有一堆亂七八糟的疑難雜症就是了。

2 thoughts on “在 Mac OS X/iOS 上指定中文排序

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.