сделать сортировку/сравнение python так же, как сортировка GNU

После некоторых первых тестов кажется, что Python использует тот же порядок сортировки, что и сортировка Linux (сортировка gnu), с порядком сортировки C (если для локали установлено значение «C»).

Однако я хотел бы иметь возможность писать код Python, который сортирует и сравнивает так же, как сортировка gnu, в зависимости от локали.

Небольшой пример кода для иллюстрации проблемы:

import os 
import subprocess

words = [
    "Abd",
    "éfg",
    "aBd",
    "aBd",
    "zzz",
    "ZZZ",
    "efg",
    "abd",
    "fff",
    ]

with open("tosort", "w") as fout:
    for word in words:
        fout.write(word + "\n")

os.environ["LC_ALL"] = "en_US.UTF-8" 
proc = subprocess.Popen(["sort", "tosort"], stdout=subprocess.PIPE)
sort_en_utf = proc.stdout.read().decode('utf-8').split()

os.environ["LC_ALL"] = "C" 
proc = subprocess.Popen(["sort", "tosort"], stdout=subprocess.PIPE) 
sort_c = proc.stdout.read().decode('utf-8').split()

os.environ["LC_ALL"] = "en_US.UTF-8"
sort_py = sorted(words)

for row in zip(sort_en_utf, sort_c, sort_py):
    print(" ".join(row))

Если приведенный выше код запущен, я получаю следующий вывод:

abd Abd Abd
aBd ZZZ ZZZ
aBd aBd aBd
Abd aBd aBd
efg abd abd
éfg efg efg
fff fff fff
zzz zzz zzz
ZZZ éfg éfg

столбец 1 - это порядок сортировки/сравнения, который я хотел бы иметь в своем коде python, если локаль "en_US.UTF-8", столбцы 2 и 3 показывают, что python сортирует так же, как сортировка linux, если локаль установить на «С».

Поэтому я также хотел бы знать, есть ли способ:

"éfg" < "fff" дает Истинно. Я не настаиваю на операторе сравнения, я также могу вызвать функцию. но результат заказа должен учитывать текущую локаль.


person gelonida    schedule 01.10.2019    source источник
comment
Связано: stackoverflow.com/q/4836710/674039   -  person wim    schedule 02.10.2019
comment
посмотрю нацсорт. проверит, особенно, уважает ли эта библиотека локаль. и будет ли он также сортировать буквы, такие как ß (должен сортироваться как ss)   -  person gelonida    schedule 02.10.2019
comment
Только что проверил нацсорт. это интересно, но делает что-то другое. например, он будет упорядочивать числа в строках по-разному, например. "a2" < "a10", поэтому он будет вести себя не так, как сортировка без параметров, а как локаль с UTF.   -  person gelonida    schedule 02.10.2019
comment
документ natsort упоминает PyICU pypi.org/project/PyICU теперь рассмотрит этот   -  person gelonida    schedule 02.10.2019
comment
Если вам нужно, чтобы это было надежно, лучшим способом может быть вызов сортировки bash из python :)   -  person wim    schedule 02.10.2019
comment
Спасибо, Вим. Ваше предложение просмотреть другую статью заставило меня перейти по нескольким ссылкам, которые в конце концов привели меня к howto/sorting.html, где я должен был искать в первую очередь.   -  person gelonida    schedule 02.10.2019
comment
@wim, ну, это то, что я делаю в данный момент (вызов подпроцесса, сортировка), но мне это не очень нравится, хотя команда сортировки работает быстро и возвращается к сортировке слиянием, если файлы действительно огромные , поэтому нет опасности потреблять всю оперативную память. На самом деле у меня была одна проблема, когда я сортировал список в командной строке, а затем обрабатывал результат с помощью скрипта Python. Тот факт, что оба не согласились с одним и тем же порядком, стоил мне довольно много времени на отладку.   -  person gelonida    schedule 02.10.2019
comment
моя вина. изменит название. И вы правы, bash sort была не очень хорошей формулировкой   -  person gelonida    schedule 02.10.2019


Ответы (1)


Хм, как-то я проглядел это:

Документ по сортировке python https://docs.python.org/3.5/howto/sorting.html в последнем разделе «Разное» упоминается функция locale.strxfrm() (см. https://docs.python.org/3.5/library/locale.html#locale.strxfrm ) в качестве ключевой функции для сортировки и locale.strcoll() в качестве функции сравнения .

Таким образом, следующий модифицированный код почти в порядке, за исключением того, что функция сравнения не возвращает напрямую значение true/false, но в моем контексте это нормально.

import subprocess

words = [
    "Abd",
    "éfg",
    "aBd",
    "aBd",
    "zzz",
    "ZZZ",
    "efg",
    "abd",
    "fff",
    "sra",
    "ssa",
    "ssb",
    "stb",
    "ßaa",
    ]

val1 = "ßaa"
val2 = "ssb"

with open("tosort", "w") as fout:
    for word in words:
        fout.write(word + "\n")

os.environ["LC_ALL"] = "en_US.UTF-8"
proc = subprocess.Popen(["sort", "tosort"], stdout=subprocess.PIPE)
sort_en_utf = proc.stdout.read().decode('utf-8').split()

os.environ["LC_ALL"] = "C"
proc = subprocess.Popen(["sort", "tosort"], stdout=subprocess.PIPE)
sort_c = proc.stdout.read().decode('utf-8').split()

locale.setlocale(locale.LC_ALL, "en_US.UTF-8")
sort_py1 = sorted(words, key=lambda v: locale.strxfrm(v))
print("%r < %r = %s , but locale.strcoll(%r, %r) = %s for %s"
      % (val1, val2, val1 < val2, val1, val2,
         locale.strcoll(val1, val2), locale.getlocale())
      )

locale.setlocale(locale.LC_ALL, "C")
sort_py2 = sorted(words, key=lambda v: locale.strxfrm(v))
print("%r < %r = %s , but locale.strcoll(%r, %r) = %s for %s"
      % (val1, val2, val1 < val2, val1, val2,
         locale.strcoll(val1, val2), locale.getlocale())
      )

for row in zip(sort_en_utf, sort_py1, sort_c, sort_py2):
    print(" ".join(row))

Результат будет

'ßaa' < 'ssb' = False , but locale.strcoll('ßaa', 'ssb') = -1 for ('en_US', 'UTF-8')
'ßaa' < 'ssb' = False , but locale.strcoll('ßaa', 'ssb') = 1 for (None, None)
abd abd Abd Abd
aBd aBd ZZZ ZZZ
aBd aBd aBd aBd
Abd Abd aBd aBd
efg efg abd abd
éfg éfg efg efg
fff fff fff fff
sra sra sra sra
ssa ssa ssa ssa
ßaa ßaa ssb ssb
ssb ssb stb stb
stb stb zzz zzz
zzz zzz ßaa ßaa
ZZZ ZZZ éfg éfg
person gelonida    schedule 01.10.2019
comment
теперь ищем лучший способ установить языковой стандарт на языковой стандарт вызывающей оболочки. Ничего лучше locale.setlocale(locale.LC_ALL, os.environ.get("LC_ALL", os.environ.get("LANG", "C"))) не придумал - person gelonida; 02.10.2019