icu:根据 2 个不同的语言环境对字符串进行排序

问题描述 投票:0回答:3

您可能知道,某些(也许是大多数)语言中的字母顺序与 Unicode 中的字母顺序不同。这就是为什么我们可能想要使用

icu.Collator
来排序,就像这个 Python 示例:

from icu import Collator, Locale
collator = Collator.createInstance(Locale("fa_IR.UTF-8"))
mylist.sort(key=collator.getSortKey)

这非常适合波斯弦。但它也会将所有波斯语字符串排序在所有 ASCII / 英语字符串之前(这与 Unicode 排序相反)。

如果我们想在给定的区域设置之前对 ASCII 进行排序怎么办?

或者理想情况下,我想按 2 个或多个区域设置进行排序。 (例如,为

Locale
提供多个
Collator.createInstance
参数)

如果我们可以告诉

collator.getSortKey
返回其他语言环境的空字节,那么我可以创建一个包含 2 个
collator.getSortKey()
结果的元组,例如:

from icu import Collator, Locale

collator1 = Collator.createInstance(Locale("en_US.UTF-8"))
collator2 = Collator.createInstance(Locale("fa_IR.UTF-8"))

def sortKey(s):
    return collator1.getSortKey(s), collator2.getSortKey(s)

mylist.sort(key=sortKey)

但看起来

getSortKey
总是返回非空字节。

python sorting locale icu
3个回答
3
投票

回答问题有点晚了,但在这里供以后参考。

ICU 校对使用 CLDR 校对算法,该算法是 Unicode 校对算法的剪裁。默认排序规则称为根排序规则。不要考虑具有一组排序规则的区域设置,更多地考虑区域设置指定区域设置所需的排序规则与根排序规则之间的任何差异。 CLDR 采用极简方法,您只需要包含基于根排序规则所需的最小差异集。

英语使用根语言环境。没有剪裁。另一方面,波斯语需要一些规则来覆盖根排序规则的某些方面。

如问题所示,波斯语排序规则将阿拉伯字符排序在拉丁字符之前。在波斯语的排序规则集中有一个规则

[reorder Arab]
。您需要重写此规则。

有几种方法可以做到这一点:

  1. icu.RuleBasedCollator
    与波斯语的自定义规则集一起使用。
  2. 创建标准波斯语排序规则,检索规则,删除重新排序指令,然后通过
    icu.RuleBasedCollator
    使用修改后的规则。
  3. 使用 BCP-47 语言标签而不是区域设置标识符创建整理器实例

还有其他方法,但第三种是最简单的:

loc = Locale.forLanguageTag("fa-u-kr-latn-arab")
collator = Collator.createInstance(loc)
sorted(mylist, key=collator.getSortKey)

这将重新排序波斯语排序规则,将拉丁字母放在阿拉伯字母之前,然后是其他所有内容。

更新2024-06-27

上面的重新排序指令首先对拉丁语重新排序,然后是阿拉伯语脚本,然后基于默认顺序的所有其他内容。

这对于波斯语和使用拉丁文字的语言的双语数据效果很好,但可能不太适合多文字数据。

有一个特殊的ISO 15924代码

Zzzz
代表未知脚本,作为ICU重排序代码,它用于表示重排序中未特别指定的所有脚本。因此
fa-u-kr-latn-arab
fa-u-kr-latn-arab-Zzzz
相同,但如果我们使用
fa-u-kr-Zzzz
而不提及其他代码,整理器将按照根整理顺序对脚本进行排序。这将为我们提供波斯语特定排序与根排序规则的默认脚本顺序相结合:

import icu
data = ["Salâm", "سلام", "тасли́м", "Persian", "فارسی", "Персидский язык"]

# Persian (Farsi) locale based collator
loc_fa = loc = icu.Locale('fa')
collator_fa = icu.Collator.createInstance(loc_fa)
sorted(data, key=collator_fa.getSortKey)
# ['سلام', 'فارسی', 'Persian', 'Salâm', 'Персидский язык', 'тасли́м']


# Persian (Farsi) locale based collator with reordering: Latin, Arabic, then other scripts
loc_alt = icu.Locale.forLanguageTag("fa-u-kr-latn-arab")
collator_alt = icu.Collator.createInstance(loc_alt)
sorted(data, key=collator_alt.getSortKey)
# ['Persian', 'Salâm', 'سلام', 'فارسی', 'Персидский язык', 'тасли́м']

# Persian (Farsi) locale based collator with reordering:  Other (Zzzz - Unknown script)
# Sets order to default CLDR order
loc = icu.Locale.forLanguageTag("fa-u-kr-Zzzz")
collator = icu.Collator.createInstance(loc)
sorted(data, key=collator.getSortKey)
# ['Persian', 'Salâm', 'Персидский язык', 'тасли́м', 'سلام', 'فارسی'

2
投票

对于 ASCII-before-locale 排序,您只需检查字符串是否为 ASCII 即可:

def sortKey(s):
    """ASCII strings first"""
    return (not s.isascii()), collator.getSortKey(s)

对于多种语言,

icu
是不明确的,例如
"Dobrý večer"
字符串是捷克语还是斯洛伐克语?此外,有很多语言都有多个纯 ASCII 单词。


对于蟒蛇<3.7, use:

def is_not_ascii(s):
    return any(ord(c) > 128 for c in s)

1
投票

不可能告诉

collator.getSortKey()
返回其他语言环境的空字节,但您可以使用按所需顺序返回所需排序键的元组的函数来控制排序行为。

def sort_key(s):
    return (collator1.getSortKey(s), collator2.getSortKey(s)) if s.isascii() else (collator2.getSortKey(s), collator1.getSortKey(s))

mylist.sort(key=sort_key)
© www.soinside.com 2019 - 2024. All rights reserved.