如何基于ICU实现多区域设置字符串排序及ASCII优先排序?
解决ICU Collator多区域排序及ASCII前置问题
核心思路:按字符所属区域分层生成排序键
要实现ASCII字符串排在波斯语(或其他区域语言)之前,或是基于多区域设置排序,关键是先区分字符串的字符所属区域,再针对性生成排序键——直接拼接不同区域的getSortKey结果无效,因为getSortKey对任何字符串都会生成对应区域的排序值,哪怕字符不属于该区域。
方案一:先分组再按区域排序
先判断字符串是否包含目标非ASCII区域的字符,给ASCII组分配更高优先级,再在组内用对应区域的Collator排序。
示例代码:
from icu import Collator, Locale import re # 初始化不同区域的排序器 en_collator = Collator.createInstance(Locale("en_US.UTF-8")) fa_collator = Collator.createInstance(Locale("fa_IR.UTF-8")) # 匹配波斯语字符的正则(可根据需求调整字符范围) fa_char_pattern = re.compile(r'[\u0600-\u06FF]') def custom_sort_key(s): # 第一步:判断是否含波斯语字符,ASCII组优先级为0(靠前),波斯语组为1(靠后) has_fa_chars = bool(fa_char_pattern.search(s)) priority = 0 if not has_fa_chars else 1 # 第二步:组内按对应区域规则排序 if priority == 0: return (priority, en_collator.getSortKey(s)) else: return (priority, fa_collator.getSortKey(s)) # 测试用例 mylist = ["سلام", "apple", "دنیا", "Banana", "123"] mylist.sort(key=custom_sort_key) print(mylist) # 输出: ['123', 'apple', 'Banana', 'سلام', 'دنیا']
方案二:自定义规则实现多区域排序(进阶)
通过ICU的RuleBasedCollator自定义排序规则,直接指定ASCII字符的优先级高于目标区域字符。
示例代码:
from icu import RuleBasedCollator # 自定义排序规则:先排ASCII字母、数字,再按波斯语规则排序 # 规则语法可参考ICU官方规范,以下为简化示例 custom_rules = """ &[before 0000] < a,A < b,B < c,C < d,D < e,E < f,F < g,G < h,H < i,I < j,J < k,K < l,L < m,M < n,N < o,O < p,P < q,Q < r,R < s,S < t,T < u,U < v,V < w,W < x,X < y,Y < z,Z < 0 < 1 < 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 &[\u0600-\u06FF] """ collator = RuleBasedCollator(custom_rules) mylist = ["سلام", "apple", "دنیا", "Banana", "123"] mylist.sort(key=collator.getSortKey) print(mylist)
为什么直接拼接SortKey无效?
Collator.getSortKey会将任何字符串映射到当前区域的排序规则中,比如波斯语Collator处理英文时,会把英文字符对应到波斯语排序体系的某个位置,导致拼接后的元组无法实现预期的分组排序逻辑。而先判断字符所属区域再分配优先级的方式,能明确区分不同组的排序层级。
内容的提问来源于stack exchange,提问作者saeedgnu
相关产品推荐
相关产品推荐

