淺談iOS開發(fā)中多語(yǔ)言的字符串排序

一、前言

在iOS開發(fā)中,一個(gè)經(jīng)常的場(chǎng)景是利用tableview展示一組數(shù)據(jù),以很多首歌曲為例子。為了便于查找,一般會(huì)把這些歌曲按照一定的順序排列,還會(huì)加上索引條以便于快速定位。
由于歌曲名可能有數(shù)字、中文、英文、韓文、日文等,要處理多語(yǔ)言文字的排序,有兩個(gè)問(wèn)題

  1. 對(duì)歌曲名進(jìn)行排序時(shí)的規(guī)則是什么呢?知道了規(guī)則以后,應(yīng)該調(diào)用哪個(gè)函數(shù)呢?
  2. 如何獲取每一首歌名的索引項(xiàng)呢?

二、多語(yǔ)言排序的規(guī)則

Objective C 中字符串使用NSString這個(gè)類表示,每一個(gè)NSString其實(shí)就是若干UTF–16 code unit排列起來(lái)。而UTF-16其實(shí)是Unicode這個(gè)組織指定的一種編碼方法,那么Unicode有沒(méi)有指定一個(gè)通用的排序方法呢?通過(guò)搜索,找到了這篇文檔。

This report is the specification of the Unicode Collation Algorithm (UCA), which details how to compare two Unicode strings while remaining conformant to the requirements of the Unicode Standard. The UCA also supplies the Default Unicode Collation Element Table (DUCET) as the data specifying the default collation order for all Unicode characters.

也就是說(shuō)Unicode通過(guò)文檔指定了Unicode字符—也是iOS處理字符串的方式—排序的規(guī)則。
其中提到指定不同的語(yǔ)言或地區(qū) (different languages or locales),最終排列的順序也會(huì)不一樣。

三、系統(tǒng)提供的字符串排序函數(shù)

Localized string comparisons are based on the Unicode Collation Algorithm, as tailored for different languages by CLDR (Common Locale Data Repository).

根據(jù)文檔,系統(tǒng)已經(jīng)幫我們實(shí)現(xiàn)了Unicode指定的排序算法,不過(guò)需要我們指定一個(gè)語(yǔ)言或地區(qū)。
系統(tǒng)為NSString提供了兩個(gè)函數(shù)- compare:,以及更加參數(shù)化的函數(shù)- compare:options:range:locale:。
第一個(gè)函數(shù)會(huì)調(diào)用第二個(gè)函數(shù),傳入一些默認(rèn)參數(shù)值。其中locale參數(shù)會(huì)傳入nil。
我們以一個(gè)例子比較一下這兩個(gè)函數(shù)比較多語(yǔ)言字符串時(shí)的區(qū)別。

- (void)testExample
{
    NSArray *rawArray = @[@"右手",@"邊城",@"重慶",@"區(qū)別",@"重要",@"行伍",@"區(qū)別",@"行走",@"nia",@"niz",@"a",@"z",@"32",@"laLa",@"la La",@"la{La",@"la<La",@"la3La"];
    [SortString defaultSort:rawArray];
    [SortString localSort:rawArray];
}

+ (void)defaultSort:(NSArray<NSString*> *)stringsToSort
{
    
    NSArray *sortedArray = [stringsToSort sortedArrayUsingComparator:^NSComparisonResult(NSString *  _Nonnull obj1, NSString *  _Nonnull obj2) {
        return [obj1 compare:obj2 options:NSCaseInsensitiveSearch];
    }];
    NSLog(@"after %@ , result is %@",NSStringFromSelector(_cmd),sortedArray);
}

+ (void)localSort:(NSArray<NSString*> *)stringsToSort
{
    NSLocale *locale=[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"];
    NSArray *sortedArray = [stringsToSort sortedArrayUsingComparator:^NSComparisonResult(NSString *  _Nonnull obj1, NSString *  _Nonnull obj2) {
        NSRange string1Range = NSMakeRange(0, [obj1 length]);
        return [obj1 compare:obj2 options:0 range:string1Range locale:locale];
    }];
    NSLog(@"after %@ , result is %@",NSStringFromSelector(_cmd),sortedArray);
}

結(jié)果如下

2017-01-23 20:14:22.318 testStringSort[40716:4054566] after defaultSort: , result is (32,a,la La,la3La,la<La,laLa,la{La,nia,niz,z,區(qū)別,區(qū)別,右手,行伍,行走,邊城,重慶,重要,)
2017-01-23 20:14:22.319 testStringSort[40716:4054566] after localSort: , result is (32,邊城,重慶,區(qū)別,區(qū)別,行伍,行走,右手,重要,a,la La,la{La,la<La,la3La,laLa,nia,niz,z,)

有幾個(gè)不同之處

  1. 數(shù)字、英文、中文順序不同
    默認(rèn)的是數(shù)字、英文、中文。指定地區(qū)信息的順序是數(shù)字、中文、英文,這符合我們的一般順序,和蘋果自帶的通訊錄軟件也相同。
    默認(rèn)是數(shù)字、英文、中文的順序是因?yàn)樵赨nicode的編碼中,數(shù)字在英文前面,英文又在中文前面。

  2. “邊城”、“右手”這兩個(gè)字符串順序不同
    默認(rèn)的排序方法,順序是“右手”、“邊城”。指定地區(qū)信息的順序是“邊城”、“右手”,和漢語(yǔ)拼音的順序一致。
    由于“右”的Unicode編碼是U+53F3,“邊”的Unicode編碼是U+8FB9,比U+53F3大,所以被排到了“右”的后面。實(shí)際上,常用的漢字編碼都在Unicode的基本多語(yǔ)言平面 (Basic Multilingual Plane) 上,而這些漢字的編碼順序是按照部首排序的,而不是拼音順序。因此使用默認(rèn)的排序方法會(huì)很混亂。

  3. 漢語(yǔ)多音字處理

    1. 識(shí)別成功的例子:“重 (chóng) 慶”、“重 (zhòng)要”
      指定地區(qū)信息以后,成功的把“右手”放在了“重慶”、“重要”之間。
    2. 識(shí)別失敗的例子:“行 (háng) 伍”、“行 (xíng) 走”
      區(qū) (qū) 別、行 (háng) 伍、行 (xíng) 走,顯然是不符合漢語(yǔ)拼音順序的。

    可見(jiàn)對(duì)于某些多音字,指定了地區(qū)信息以后,系統(tǒng)甚至可以識(shí)別成功!對(duì)于另一些,系統(tǒng)還做不能識(shí)別成功。

  4. 特殊字符的順序不同
    按照ASCII碼,空格、數(shù)字、< 、大寫字母、{ 依次遞增,默認(rèn)排序方法的結(jié)果也印證了這一點(diǎn)。
    指定了地區(qū)信息以后,排序結(jié)果是空格、{ 、< 、數(shù)字、大寫字母,有了一定變化。

從上面結(jié)果可以看出,排序時(shí)不要用默認(rèn)的比較方法,要用指定了地區(qū)的排序方法。

其他函數(shù)

系統(tǒng)提供了一個(gè)函數(shù)- localizedStandardCompare:,根據(jù)注釋,其排序結(jié)果和Finder一樣,根據(jù)需要,也可選擇這個(gè)函數(shù)。

This method should be used whenever file names or other strings are presented in lists and tables where Finder-like sorting is appropriate. The exact sorting behavior of this method is different under different locales and may be changed in future releases. This method uses the current locale.

三、獲取字符串的索引項(xiàng)

系統(tǒng)專門提供了一個(gè)類UILocalizedIndexedCollation來(lái)幫助我們完成這項(xiàng)工作。

The UILocalizedIndexedCollation class is a convenience for organizing, sorting, and localizing the data for a table view that has a section index.

此外,蘋果還提供了一個(gè)例子,解釋了UILocalizedIndexedCollation的用法。

四、另一個(gè)常用卻有問(wèn)題的方法

網(wǎng)上的另一個(gè)方法是把BMP中的漢字的拼音首字母全部列出來(lái),以數(shù)組方式存儲(chǔ)。得到漢字以后,把對(duì)應(yīng)的Unicode值減去第一個(gè)漢字的Unicode值,得到一個(gè)值,把這個(gè)值作為下標(biāo),從數(shù)組中取得對(duì)應(yīng)的拼音首字母。
這么做有幾個(gè)缺點(diǎn)

  1. 漢字不僅分布在BMP,也分布在擴(kuò)展平面,這個(gè)方法無(wú)法處理擴(kuò)展平面的漢字。擴(kuò)展平面的漢字雖然不常用,但總有用到的可能。
  2. 當(dāng)拼音首字母相同時(shí),無(wú)法處理兩個(gè)字的順序。
    比如漢字"你"和漢字"您"的拼音首字母都是“n”,這個(gè)方法就只能按照Unicode編碼順序排列了,在BMP中,也就是部首序。

五、總結(jié)

既然系統(tǒng)提供了方法,我們最好還是按照系統(tǒng)的方法來(lái)實(shí)現(xiàn)好了。

  1. 指定地區(qū),讓系統(tǒng)處理多語(yǔ)言字符串的排序工作。
  2. 利用UILocalizedIndexedCollation類,獲取字符的索引。

六、參考

  1. NSString 與 Unicode
  2. Objective-C 中文 按拼音全排序
  3. UNICODE COLLATION ALGORITHM
  4. Internationalizing Your Code
  5. Language and Locale IDs
  6. UILocalizedIndexedCollation
  7. UITableView Fundamentals for iOS
  8. YUChineseSorting
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容