JavaScript 中文排序問題

今天才發現 JavaScript 中文字串排序有個大問題! 下圖是 KendoGrid 在 Chrome 使用 JavaScript 排序的結果,如圖所示,一到七由小到大排序結果為一、七、三、二、五、六、四,既不是依筆劃,也不是依注音: (SQL 的中文定序就區分筆劃跟注音,例如: Chinese_Taiwan_Stroke_CI_AS vs Chinese_Taiwan_Bopomofo_CI_AS 參考)

爬文後得知這是 JavaScript 中文字串排序的己知問題(我 Lag 真大),字串型別有個 localeCompare() 可傳入語系參數進行比較,貌似能解決問題但實際不行。

我寫了測試程式如下。測試字元陣列擷取自 BIG5 字碼表,前面的 BIG5 內碼較小,筆劃較少,並插花加入黑、暗兩個超過五劃的字。分別測試三種排序方法,分別是 直接使用 sort()、sort() 搭配 localeCompare() 不指定語系、sort() 搭配 localeCompare() 指定語系,排序結果以 document.write() 印出。

<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 />")
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>

以下擷圖分別是 Edge、IE、Firefox 的執行結果,如圖所示,內建 sort() 順序即一開始 KendoGrid 案例的排序結果,而 localeCompare 排序與 localeCompare zh-TW 排序結果相同,雖然與 BIG5 順序有一些出入,至少有遵循筆劃由少到多的順序。

內建 sort() 應是依據 UTF-8 Byte 排序,數字部分依序為一、七、三、二、六、四,使用 Encoding.UTF8.GetBytes() 轉成位元組可證實推測:

同樣的測試在 Chrome 及 Safari 就精采了,sort() 與 localeCompare() 不指定語系的排序結果相同,即上述 UTF8 轉 Byte 後的順序;而 localeCompare() 指定 zh-TW 語系的排序結果讓人莫名其妙,暗字衝到第一個,我說不出是依什麼規則。(2017-10-20 補充,zh-TW 被 Chrome 誤為簡體漢語拼音,請見文章留言及文末補充說明)

另外我還試了用 C# 排序,其結果與 Edge、IE、Firefox 的 localeCompare() 排序順序一致。

由以上測試,我的結論是 localeCompare() 無法跨瀏覽器實現符合預期且一致的中文字串排序,可能因瀏覽器實作不同出現非預期結果,若求保險還是在 C# 或資料庫端處理排序為上。

2017-10-20 更新

貼文後蒙網友 Hsi-Lien Chin、diberium 留言解惑,在 Chrome 使用 localeCompare 排序正體中文時語系參數應傳入 zh-Hant 或 zh-Hant-TW,實測 Chrome 執行 localeCompare(b, "zh-Hant") ,結果就與 Edge/IE/Firefox 一致了。特此感謝。

歡迎推文分享:
Published 19 October 2017 11:03 PM 由 Jeffrey
Filed under: ,
Views: 3,764



意見

# diberium said on 19 October, 2017 10:58 AM

看你的测试,chrome是汉语拼音规则…

暗(An)

八(Ba)

丁(Ding)

二(Er)

公(Gomg)

黑(Hei)

九(Jiu)

久(Jiu)

力(Li)

六(Liu)

……

# Jeffrey said on 19 October, 2017 05:14 PM

to diberium, 感謝解惑,Chrome localeCompare() 語系參數傳入"zh-Hant",其排序就會和 Edge/IE/Firefox一致,已補充本文。

# jackyrong said on 15 November, 2017 02:59 AM

那如果KENDO GRID的列排序中,有中文,数字,英文混合的话,如何比较好的进行排序呢?

# Jeffrey said on 15 November, 2017 04:29 AM

to jackyrong, 如果 localeCompare() 排序不符需求,可改在伺服器端排序,再不符要求,可考慮置換字元產生排序專用字串。

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<October 2017>
SunMonTueWedThuFriSat
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234
 
RSS
創用 CC 授權條款
【廣告】
twMVC

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication