【问题标题】:Sort Cyrillic strings before Latin in python在python中对拉丁语之前的西里尔字符串进行排序
【发布时间】:2020-06-17 19:00:48
【问题描述】:

在我的数据库中,我有西里尔文和拉丁文字符的记录。默认情况下,它们按字母顺序排列,拉丁记录在前:

abc...bcd...cde...абв...

我想把西里尔字母放在第一位:

абв...abc...bcd...cde...

到目前为止我所尝试的:

  1. This solution。它不是那么好,因为它只按 第一个单词,我可以同时使用西里尔文和拉丁文单词 字符串(甚至是同一个单词中的混合字符)。

  2. 用西里尔字母和拉丁字母编写我自己的列表。它有效,但 一点都不好。我无法考虑所有可能的字母 在两个字母中,包括带有变音符号的字母并写下它们 下来。

我也一直在研究 PyICU,但不知道如何使用它。

我的猜测是我应该在这里使用一些自定义排序规则。问题是如何在实践中做到这一点。

【问题讨论】:

  • 那么,您想要达到的确切顺序是什么? “我想把西里尔字母放在第一位”——任何包含西里尔字母的字符串都应该排在全拉丁字符串之前吗?
  • @ZaurNasibov 不是真的。我希望每个西里尔字符都出现在拉丁字符之前。例如,“waюnt”(带有一个西里尔字母)应该出现在“wannt”之前:这两个词的第三个字母不同,而西里尔字母位于拉丁语之前。但是如果我们有 'wqюнт' 和 'wannt',这里 'wannt' 应该排在第一位,因为我们比较第二个字母,并且在拉丁字母表中,'a' 在 'q' 之前。
  • 您可以在sortedlist.sort 中使用key 函数,如果第一个字符串字符是西里尔文,则为0,否则为1。
  • @PanagiotisKanavos 我需要考虑每个字符,而不仅仅是第一个。
  • @snakecharmerb PostgreSQL。但排序实际上是在从数据库中检索到字符串之后发生的。

标签: python sorting unicode collation


【解决方案1】:

根据第一个字符进行排序的非常肮脏但有效的解决方案。它还消除了字母记录的差异。

ls = ['32', '24', 'xyz', 'WYZ', 'abc', 'абв', 'КЛМ', 'эюя', 'еёж', 'ёжз', '', '_']

def sort_rule(st):
    st = st.lower()
    ch = st[0] if st else ''       
    if ch >= 'а' and ch <= 'я':
        st = '1' + st
    elif ch == 'ё':
        st = '1е' + st
    elif ch >= 'a' and ch <= 'z':
        st = '2' + st
    else:
        st = '3' + st
    return st

sorted(ls, key=sort_rule)

> ['абв', 'еёж', 'ёжз', 'КЛМ', 'эюя', 'abc', 'WYZ', 'xyz', '', '24', '32', '_']

为了比较,默认排序给出下一个结果:

sorted(ls)

> ['', '24', '32', 'WYZ', '_', 'abc', 'xyz', 'КЛМ', 'абв', 'еёж', 'эюя', 'ёжз']

【讨论】:

  • 问题是关于西里尔文,而不是俄文。此答案仅涵盖俄语。
【解决方案2】:

如果是拉丁字符,您可以尝试通过在每个字符前面加上 1 或在其他字符前面加上 0 来生成密钥:

sorted(items, key = lambda item : ['1' + x if x < '\x7f' else '0' + x for x in item])

【讨论】:

  • á 也是一个拉丁字母。测试西里尔文,因为它只包含在几个 Unicode 子范围中。好主意,仍然。
【解决方案3】:

IMO 这不是一件小事。我会说确实需要排序规则。

因此,假设一个键函数会将字符串转换为代码点元组,其中所有非西里尔文代码点将移动 100000):

import unicodedata

def key(s):
    SHIFT = 100000
    return tuple(
        ord(c) if is_cyrillic(c) else ord(c) + SHIFT
        for c in s
    )

def is_cyrillic(c):
    return unicodedata.name(c).startswith('CYRILLIC')        


>>> sorted(('wannt', 'waюnnt'), key=key)
Out[34]: ['waюnnt', 'wannt']

is_cyrillic 可以通过使用初步表或缓存数据库字符串中的西里尔字符来优化。

【讨论】:

  • 一种优化可能是根据西里尔字母范围 (U+0400-U+04FF) 检查字符的代码点。
  • @PanagiotisKanavos,好点子!这会快得多。对于全系列,还必须检查所有扩展和补充。
【解决方案4】:

一种方法是使用transliterate modulecytranslit 并使用将所有内容音译为所需字母的排序键:

import transliterate

items = ['abc', 'bcd', 'cde', 'абв']

print(sorted(items, key=lambda x: transliterate.translit(x, 'ru')))

输出是期望的

['абв', 'abc', 'bcd', 'cde']

【讨论】:

  • 俄语字母不是我想要的。我需要西里尔字母,而不是俄语。更清楚地说,所有基于西里尔文的字符。
  • 音译模块很好用,不知道:)
  • 它之所以有效,是因为示例中的所有拉丁字符串都被音译为排序高于“абв”的俄语字符串。如果示例中的第一个字符串是“aba”,而不是“abc”,则它不起作用。它还会完全搞乱纯拉丁字符串的排序,例如 sorted('ccc', 'ddd') 会给你 ('ddd', 'ccc')
  • @Edmond 我也添加了对cytranslit 的引用。它包含您可能可以使用的映射表。 github.com/opendatakosovo/cyrillic-transliteration/blob/master/…
  • 好的,感谢您的建议和更正,但这绝对不符合我的要求。
猜你喜欢
  • 2012-06-19
  • 2017-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多