iOS App Objective-C Swift JAVA C# JavaScript

中文字的筆劃排序

陳傑雄 2018/05/02 12:20:37
4831

中文字的筆劃排序


簡介

介紹中文字如何依照筆劃排序

作者

陳傑雄


中文字的筆劃排序

先前某個專案遇到一個需求:有一個陣列,裡面有一堆中文姓名字串資料,客戶希望這些包含姓名字串的 陣列資料,可以依照特別的中文索引方式排序,像是使用筆劃數、拼音...等等。
花時間研究之後,發現只要在 compare 的時候,選擇特定的 Locale 就好了,從研究 iOS 的案例開始,我們也發現在目前版本的 JAVAC# 和 JavaScript 也已經支援這種中文排序方式了, 所以特別記錄下來與大家分享。 
 
緣起:在Mac OS X / iOS 上指定中文筆劃排序
如前面所講的,專案需求的原因,才開始研究如何對中文字依筆劃數做索引排序,雖說最後發現只要選擇特定的 Locale 就可以,但是 Apple Inc. 本身的文件中,居然對於有這些 Locale 可以設定卻沒有什麼著墨,顯然地,很多時候光看 Apple 的文件是不夠的。
在 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)。所幸的是,目前   Cocoa Framework 已經幫我們做好這些事了, 直接呼叫它,寫好 Objective-C 或 Swift 就好了。
這邊我寫了一些小小的範例程式,看看 Swift 和 Objective-C 怎麼對陣列裡的資料完成中文筆劃排序:
 
Swift
// 定義一個中文字串資料的陣列
var strArray:[String] = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]
 
// 宣告文化特徵 locale 為 zh_TW
let locale = Locale(identifier: "zh_TW")
 
// 對陣列做比對和升冪排序
strAry = strAry.sorted(by: { $0.compare($1, locale: locale) == .orderedAscending})
 
// 將排序後的陣列逐一印出
for str in  strAry {
        print(str)
}
 

 Objective-C

// 定義一個中文字串資料陣列
NSArray* strAry = @[@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", @"", @"", @"", @"", @"", @""];

// 宣告文化特徵 locale 為 zh,並指定以筆劃排序
NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh@collation=stroke"];

// 對陣列做比對和升冪排序
strAry = [strAry sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        return [obj1 compare:obj2 options:0 range:NSMakeRange(0, [obj1 length]) locale:locale];
}];

// 將排序後的陣列逐一印出
for (NSString* s in strAry) {
        NSLog(@"%@", s);
} 

 

延伸:在其他程式語言中指定中文筆劃排序
接著我們想到在其他程式語言中是否也能以相同的方式,來做到對中文字串資料做筆畫排序呢?
至少我們在實際測試中,JAVAC#JavaScript 顯示結果是肯定的,其它的語言留給大家試試看了。
下面是其他程式語言的範例程式碼,供大家參考。
 
JAVA
public class Asc implements Comparator { 
        public int compare(Object arg0, Object arg1) { 
        VLG_ItemMaster m1 = (VLG_ItemMaster)arg0; 
        VLG_ItemMaster m2 = (VLG_ItemMaster)arg1; 
        Collator collator =  Collator.getInstance(Locale.TRADITIONAL_CHINESE); 
        return collator.compare(m1.getItemDesc(), m2.getItemDesc()); 
    }
}
 
C#
using System;
using System.Globalization;
using System.Threading;
 
public class ArraySort 
{
   public static void Main(String[] args) 
   {
      // 定義一個中文字串資料陣列
      string[] stringArray = { "趙", "錢", "孫", "李", "周", "吳", "鄭", "王", "馮", "陳", "褚", "衛", "蔣"};
 
      // 印出原字串陣列資料
      Console.WriteLine( "The original string array:");
      PrintIndexAndValues(stringArray);
 
      // 宣告文化特徵 locale 為 "zh-TW"
      Thread.CurrentThread.CurrentCulture = new CultureInfo("zh-TW");
      // 排序陣列
      Array.Sort(stringArray);
 
      // 印出排序後的新字串陣列資料
      Console.WriteLine("After sorting for the culture \"zh-TW\":");
      PrintIndexAndValues(stringArray); 
   }
   public static void PrintIndexAndValues(string[] myArray)  
   {
      for (int i = myArray.GetLowerBound(0); i <= 
            myArray.GetUpperBound(0); i++ )
         Console.WriteLine("[{0}]: {1}", i, myArray[i]);
      Console.WriteLine();      
   }
}
 
JavaScript
<html>
<body style="font-size: 9pt">
<script>
var raw = "趙錢孫李周吳鄭王馮陳褚衛蔣";
var ary = [];
for (var i = 0; i < raw.length; i++)
    ary.push(raw.substr(i, 1));
    document.write("原始順序(BIG5/筆劃)<br />")
    document.write(JSON.stringify(ary, null ," "));
    document.write("<hr />")
    ary.sort();
 
    document.write("內建 sort()<br />")
    document.write(JSON.stringify(ary, null ," "));
    document.write("<hr />")
    ary.sort(function(a,b) { return a.localeCompare(b); });
 
    document.write("localeCompare 排序<br />")
    document.write(JSON.stringify(ary, null ," "));
    document.write("<hr />")
    if (navigator.userAgent.indexOf("Chrome") != -1 
        || navigator.userAgent.indexOf("Safari") != -1) {
        // chrome 和 Safari 瀏覽器需用 "zh_Hant"
        ary.sort(function(a,b) { return a.localeCompare(b, "zh-Hant"); });  
    } else {
        // 其他瀏覽器使用 "zh-TW"
        ary.sort(function(a,b) { return a.localeCompare(b, "zh-TW"); }); 
    }
    document.write("localeCompare zh-TW 排序<br />")
    document.write(JSON.stringify(ary, null ," "));
</script>
</body>
</html>
陳傑雄