Как реализовать сопоставление строк Unicode путем сворачивания в Python
У меня есть приложение, реализующее добавочный поиск. У меня есть каталог строк юникода, которые нужно сопоставить и сопоставить их с заданной "ключевой" строкой; строка каталога является "хитом", если она содержит все символы в ключе, по порядку, и она оценивается лучше, если ключевые символы кластеризуются в строке каталога.
В любом случае, это работает нормально и точно соответствует юникоду, так что "ö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 ответов
Вы можете использовать это 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'
Для моего приложения я уже рассмотрел это в другом комментарии: я хочу получить результат Unicode и оставить необработанные символы без изменений.
В этом случае правильный способ сделать это - создать объект коллатера UCA, чья сила настроена на сравнение только по первичной силе, что, таким образом, полностью игнорирует диакритические знаки.
В этом ответе я покажу, как это сделать, используя Perl. Первый объект-сборщик имеет необходимую вам силу, а второй рассматривает акценты для разрыва связи.
Вы заметите, что при выполнении этих сравнений не пострадало ни одной строки: исходные данные не затрагиваются.
Универсальным решением (особенно для нормализации поиска и генерации слагов) является модуль unidecode:
http://pypi.python.org/pypi/Unidecode
Это порт модуля Text::Unidecode для Perl. Он не полный, но он переводит все символы латинского алфавита, которые я смог найти, транслитерирует кириллицу, китайский и т. Д. На латынь и даже правильно обрабатывает символы полной ширины.
Вероятно, хорошей идеей будет просто удалить все символы, которые вы не хотите иметь в конечном выводе, или заменить их на заполнитель (например, "äßœ$"
будет undecoded для "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()
Это заменит все не алфавитно-цифровые символы фиктивной заменой, а затем транслитерирует строку и превратит ее в строчные.
Что насчет этого:
normalize('NFKD', unicode_string).encode('ASCII', 'ignore').lower()
Взято отсюда (испанский) http://python.org.ar/pyar/Recetario/NormalizarCaracteresUnicode
Посмотрите на это: ftp://alan.smcvt.edu/hefferon/unicode2ascii.py
Возможно, не завершено, но может помочь вам начать.