Найти - Пользователи
Полная версия: сортировка русских слов компаратором
Начало » Python для новичков » сортировка русских слов компаратором
1 2 3 4 5
Pawl
Доброго времени суток. Сам я программирую в java и немного в Delphi, а тут попросили сделать задачку на питоне. Задача следущая: есть набор русских слов в файле, разделённых пробелами, пунктуацией, а также числами. Необходимо подсчитать количество одинаковых слов и вывести их на консоль в отсортированном по алфавиту словаре в виде (слово, количество). Проблема оказалась в том, что для русской локали е и ё - абсоллютно одинаковые буквы, в то время, как в алфавите е идет перед ё, т. е. слово “ель” должно становиться перед словом “ёлка”. Пришлось искать возможности создания правил сортировки. Я обнаружил, что для питона 2 в функции сортировки есть ключ cmp для компаратора, но делать компаратор для кирилицы в питоне 2 мне показалось нетривиальной задачей. У питона 3 с UTF-8 дела обстоят получше, но компаратор, как таковой, в нём использовать уже нельзя. Зато там есть костыль в виде функции cmp_to_key, которым я в итоге и воспользовался. Вот что у меня получилось:
from codecs import open
from functools import cmp_to_key
from re import split
text = open("load", "r", "utf-8")
s = text.read()
words = split('[\d\W]+', s)
d = {}
for w in words:
    if w != '':
        if w in d:
            d[w] += 1
        else:
            d[w] = 1
items = list(d.items())
def comparator(a, b):
    x = str(a)
    y = str(b)
    t = len(x) if len(x) < len(y) else len(y)
    for j in range(t):
        if x[j] == y[j]:
            continue
        if x[j] != 'ё' and y[j] != 'ё':
            if x[j] < y[j]:
                return -1
            else:
                return 1
        elif x[j] == 'ё':
            if y[j] > 'е':
                return -1
            else:
                return 1
        elif y[j] == 'ё':
            if x[j] > 'е':
                return 1
            else:
                return -1
    if len(x) < len(y):
        return -1
    elif len(x) > len(y):
        return 1
    else:
        return 0
items.sort(key=cmp_to_key(comparator))
for i in items:
    print(i)
Код работает, понятно, что он не идеален и не учитывает заглавные буквы, но это можно доделать. Меня удивляет другое. Я слышал, что у питона очень развиты библиотеки работы с текстом, но я, если честно, этого не почувствовал. В java подобный компаратор создать на порядок проще - там для этого есть класс RuleBasedCollator. Неужели в питоне для такой типовой задачи надо делать костыли? Или есть более простой путь, который я не нашел? Буду рад, если вы меня просветите.
Спасибо!
З. Ы. Вообще, питон - интересный язык, можно побаловаться. Напрягают только различия 2 и 3 версий.
FishHook
#!/usr/bin/env python
# -* coding: utf-8 -*-
d = [u'ё', u'ель', u'ёлочка', u'ёжик', u'ё моё', u'е',  u'ельник', u'ежовый']
import sys
print (sys.version)
for i in sorted(d):
    print i

2.7.5+ (default, Sep 19 2013, 13:48:49) 
[GCC 4.8.1]
е
ежовый
ель
ельник
ё
ё моё
ёжик
ёлочка

Вроде всё правильно сортирует, нет?
Rodegast
Если не правильно, то это поможет:
sorted([u"а", u"ё", u"е"], key=ord)
sander
Rodegast
>>> ord('е')
1077
>>> ord('ё')
1105
Rodegast
> в алфавите е идет перед ё
1077 < 1105
Разве что-то не так?
Pawl
sander
>>> ord('е') 1077 >>> ord('ё') 1105
понятно, а ord('я') - 1103, т. е. ё идет вообще последней буквой.
Rodegast
sorted(, key=ord)
а sorted(, key=ord) - выводит ‘е’, ‘я’, ‘ё’, т. е ё - последняя.
FishHook
d = import sys print (sys.version) for i in sorted(d): print i
тоже самое, если добавить в список слово на букву “я”, оно выведется перед словом на ё и потом, print i - это питон 2, в 3-м надо print(i), а я делаю на 3-м.
FishHook
Да, действительно, ord, кстати, не помогает.

Тупенько, зато работает
#!/usr/bin/env python
# -* coding: utf-8 -*-
d = [u'ё', u'ель', u'ямка', u'ёлочка', u'упырь', u'ёжик', u'ё моё', u'е',  u'ельник', u'ежовый', u'ягель']
ALPHABET = {i[1]: i[0] for i in enumerate(u"абвгдеёжзийклмнопрстуфхцчшщъыьэюя")}
for i in sorted(d, key=lambda x: ALPHABET[x[0].lower()]):
    print (i)
Rodegast
Да похоже что Ё это слабое место юникода Тогда как-то так:
idx = [u"а", u"ё", u"е", u"я"]
sorted([u"а", u"ё", u"е"], key=lambda x: idx.index(x[0]))
Pawl
FishHook
Тупенько, зато работает
почему-то в таком виде
from codecs import open
from re import split
text = open("load", "r", "utf-8")
s = text.read()
words = split('[\d\W]+', s)
d = {}
for w in words:
    if w != '':
        if w in d:
            d[w] += 1
        else:
            d[w] = 1
items = list(d.items())
ALPHABET = {i[1]: i[0] for i in enumerate(u"абвгдеёжзийклмнопрстуфхцчшщъыьэюя")}
for i in sorted(items, key=lambda x: ALPHABET[x[0].lower()]):
    print(i)
когда я считываю слова из файла load в словарь d, выбрасывается ошибка
Traceback (most recent call last):
File “CUsers/Pawl/IdeaProjects/pyFirst/first.py”, line 18, in <module>
for i in sorted(items, key=lambda x: ALPHABET[x.lower()]):
File “CUsers/Pawl/IdeaProjects/pyFirst/first.py”, line 18, in <lambda>
for i in sorted(items, key=lambda x: ALPHABET[x.lower()]):
KeyError: ‘метла’
Pawl
FishHook
Да похоже что Ё это слабое место юникода Тогда как-то так:
так тоже такая же ошибка
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB