2009-09-11 1 views
9

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

В любом случае, это работает отлично и соответствует Юникоду точно, так что "ОСТ" будет соответствовать "ОСТУ blocket" или "г ОСТА" или "R ö d я ы".

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

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

Как лучше реализовать этот Юникод складных Искателя в 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() 

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

+0

Это действительно круто и, вероятно, будет чрезвычайно полезно для автозаполнения имен людей, так как большинство людей не захотят вводить акценты при поиске имен. Я изучаю, как сделать что-то подобное на Java. Кажется, что это касается некоторых случаев: http://java.sun.com/javase/6/docs/api/java/text/Collator.html. –

+0

да. Обратите внимание, что вы можете отказаться от «ü, å, ä, ö» из таблицы для особых случаев, если вы хотите, чтобы они просто стали акцентами. Эти расширения дифтонга были тем, что я хотел от моего POV (более правильно унизительным языком); есть несчастливые исключения на других языках для всех таких вещей (например, испанский ü). – u0b34a0f6ae

ответ

5

Вы можете использовать thisstrip_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' 
+0

Это оставляет необработанные символы в строке, а не лишает их, что хорошо. 'Dźwięk -> Dzwiek', но 'Wyłącz -> Wyłacz'; то я могу добавить особые случаи поверх этого – u0b34a0f6ae

+0

Я принимаю этот ответ, так как фильтрация нормализации идет почти полностью. Проверьте обновленный вопрос на предмет того, что я использую. – u0b34a0f6ae

+1

Однако обратите внимание, что вы хотите отфильтровать NF ** K ** D; это разложение без строгой эквивалентности, например, символы индекса преобразуются в простые цифры. – u0b34a0f6ae

1

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

, вероятно, не полный, но, возможно, вы начали.

+0

Сценарий - хорошее вдохновение, но на самом деле я не хочу превращать его в чистый ascii (больше). Проверьте обновленный вопрос на то, что я использую сейчас. – u0b34a0f6ae

1

раствора общего назначения (особенно для поиска нормализации и генерации слизней) является модулем unidecode:

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

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

Это, вероятно, хорошая идея, чтобы просто раздеться все символы, которые вы не хотите, чтобы в конечном выходе или заменить их с наполнителем (например "äßœ$" будет unidecoded к "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() 

Это заменит все не алфавитно-цифровые символы с фиктивным замены и затем транслитерируйте строку и превратите ее в нижний регистр.

+0

Спасибо за этот отзыв. Мое приложение многоязычное, и я не уверен, хочу ли я слишком сильно его опрокинуть на строгий алиасинг ascii. Мое текущее решение имеет разные, но интересные функции, например, японский скрипт «ヘ» будет соответствовать «ペ», как «a» будет соответствовать «ä»; диакритическая зачистка более универсальна, хотя я не установил, полезен ли это или нет (у меня есть только отзывы от западных пользователей). – u0b34a0f6ae

+0

@ u0b34a0f6ae - при каких обстоятельствах вы бы хотели, чтобы «ペ» (pe) соответствовал «ヘ» (он)? – simon

1

Что об этом один:

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

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

+0

Для моего приложения я уже обратился к этому в другом комментарии: я хочу получить * unicode * result и * оставить необработанные символы нетронутыми *. – u0b34a0f6ae

4

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

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

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

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

Смежные вопросы