Как реализовать сопоставление строк Unicode путем сворачивания в Python - PullRequest
10 голосов
/ 11 сентября 2009

У меня есть приложение, реализующее добавочный поиск. У меня есть каталог строк юникода, которые нужно сопоставить и сопоставить их с заданной «ключевой» строкой; строка каталога является «хитом», если она содержит все символы в ключе по порядку, и она оценивается лучше, если кластер ключевых символов в строке каталога.

В любом случае, это прекрасно работает и точно соответствует юникоду, так что «öst» будет соответствовать « Öst blocket» или «r öst » или «r ö d st en ".

В любом случае, теперь я хочу реализовать свертывание, поскольку в некоторых случаях бесполезно проводить различие между символом каталога, таким как «á» или «é», и ключевым символом «a» или «e».

Например: «Оле» должно соответствовать «Оле»

Как мне лучше всего реализовать этот механизм сопоставления в Unicode в Python? Эффективность важна, так как я должен сопоставить тысячи строк каталога с коротким заданным ключом.

Он не должен превращать его в ASCII; фактически выходная строка алгоритма может быть в кодировке Unicode. Оставить персонажа лучше, чем убрать его.


Я не знаю, какой ответ принять, поскольку я использую немного и того, и другого. Принятие декомпозиции NKFD и удаление меток совмещения почти подходит к концу, я добавлю к этому лишь несколько пользовательских транслитераций. Вот модуль, как он выглядит сейчас: (Предупреждение, содержит встроенные символы Юникода, так как редактировать его намного приятнее.)

# -*- encoding: UTF-8 -*-

import unicodedata
from unicodedata import normalize, category

def _folditems():
    _folding_table = {
        # general non-decomposing characters
        # FIXME: This is not complete
        u"ł" : u"l",
        u"œ" : u"oe",
        u"ð" : u"d",
        u"þ" : u"th",
        u"ß" : u"ss",
        # germano-scandinavic canonical transliterations
        u"ü" : u"ue",
        u"å" : u"aa",
        u"ä" : u"ae",
        u"æ" : u"ae",
        u"ö" : u"oe",
        u"ø" : u"oe",
    }

    for c, rep in _folding_table.iteritems():
        yield (ord(c.upper()), rep.title())
        yield (ord(c), rep)

folding_table = dict(_folditems())

def tofolded(ustr):
    u"""Fold @ustr

    Return a unicode str where composed characters are replaced by
    their base, and extended latin characters are replaced by
    similar basic latin characters.

    >>> tofolded(u"Wyłącz")
    u'Wylacz'
    >>> tofolded(u"naïveté")
    u'naivete'

    Characters from other scripts are not transliterated.

    >>> tofolded(u"Ἑλλάς") == u'Ελλας'
    True

    (These doctests pass, but should they fail, they fail hard)
    """
    srcstr = normalize("NFKD", ustr.translate(folding_table))
    return u"".join(c for c in srcstr if category(c) != 'Mn')

if __name__ == '__main__':
    import doctest
    doctest.testmod()

(И для фактического соответствия, если это кого-то интересует: я заранее создаю сложенные строки для всего своего каталога и помещаю свернутые версии в уже имеющееся свойство псевдонима объекта каталога.)

Ответы [ 5 ]

6 голосов
/ 11 сентября 2009

Вы можете использовать эту strip_accents функцию для удаления акцентов:

def strip_accents(s):
   return ''.join((c for c in unicodedata.normalize('NFD', unicode(s)) if unicodedata.category(c) != 'Mn'))

>>> strip_accents(u'Östblocket')
'Ostblocket'
4 голосов
/ 05 марта 2011

Для моего приложения я уже говорил об этом в другом комментарии: я хочу получить Unicode результат и оставить необработанные символы нетронутыми.

В этом случае правильный способ сделать это - создать объект коллатера UCA с его прочностью, настроенной на сравнение только с первичной прочностью, что, таким образом, полностью игнорирует диакритические знаки.

Я показываю, как это сделать, используя Perl в этом ответе . Первый объект-сборщик имеет необходимую вам силу, а второй учитывает акценты для разрыва связи.

Вы заметите, что при выполнении этих сравнений не пострадало ни одной строки: исходные данные не затрагиваются.

1 голос
/ 03 мая 2010

А как насчет этого:

normalize('NFKD', unicode_string).encode('ASCII', 'ignore').lower()

взято отсюда (испанский) http://python.org.ar/pyar/Recetario/NormalizarCaracteresUnicode

1 голос
/ 24 февраля 2010

Универсальным решением (особенно для нормализации поиска и генерации слагов) является модуль unidecode:

http://pypi.python.org/pypi/Unidecode

Это порт модуля Text :: Unidecode для Perl. Он не полный, но он переводит все символы латинского алфавита, которые я смог найти, транслитерирует кириллицу, китайский и т. Д. На латынь и даже правильно обрабатывает символы полной ширины.

Вероятно, хорошей идеей будет просто удалить все символы, которые вы не хотите иметь в конечном выводе, или заменить их на заполнитель (например, "äßœ$" будет не кодирован до "assoe$", так что вы можете удалить символы не-буквенно-цифровой). Для символов он будет транслитерирован, но не должен (скажем, § => SS и => EU) вам необходимо очистить ввод:

input_str = u'äßœ$'
input_str = u''.join([ch if ch.isalnum() else u'-' for ch in input_str])
input_str = str(unidecode(input_str)).lower()

Это заменит все не буквенно-цифровые символы на фиктивную замену, а затем транслитерирует строку и превратит ее в строчные.

1 голос
/ 11 сентября 2009

Посмотрите на это: ftp: //alan.smcvt.edu/hefferon/unicode2ascii.py

Возможно, не завершено, но может помочь вам начать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...