2009-09-11 17 views
9

Tengo una aplicación que implementa la búsqueda incremental. Tengo un catálogo de cadenas de Unicode que se emparejarán y las uniré a una cadena de "clave" dada; una cadena de catálogo es un "acierto" si contiene todos los caracteres de la clave, en orden, y se clasifica mejor si los caracteres clave se agrupan en la cadena del catálogo.Cómo implementar la coincidencia de cadena Unicode plegando en python

De todos modos, esto funciona bien y coincide exactamente con Unicode, por lo que "öst" coincidirá con "Öst Blocket" o "r öst" o "r ö d st en".

De todos modos, ahora quiero implementar plegado, ya que hay algunos casos en los que no es útil distinguir entre un carácter de catálogo como "á" o "é" y el carácter "a" o "e".

Por ejemplo: "Ole" debe coincidir con "Olé"

¿Cómo implementar mejor este matcher Unicode-plegable en Python? La eficiencia es importante ya que tengo que unir miles de cadenas de catálogo con la clave corta dada.

No tiene que convertirlo en ascii; de hecho, la cadena de salida del algoritmo podría ser unicode. Dejar a un personaje es mejor que desnudarlo.


No sé qué respuesta aceptar, ya que uso un poco de ambos. Tomar la descomposición de NKFD y eliminar las marcas de combinación casi llega al final, solo agrego algunas transliteraciones personalizadas a eso. Aquí está el módulo como se ve ahora: (Advertencia, contiene caracteres Unicode en línea, ya que es mucho más agradable para editar esa manera.)

# -*- 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() 

(Y, para el juego real, si que interesa a nadie: construyo doblé cuerdas para todo mi catálogo de antemano, y poner las versiones dobladas en la propiedad catálogo de objetos alias ya disponible)

+0

Esto es realmente genial, y probablemente sea extremadamente útil para completar automáticamente los nombres de personas, ya que la mayoría de las personas no se molestará en introducir acentos al buscar nombres. Estoy investigando sobre cómo hacer algo similar en Java. Esto parece manejar algunos casos: http://java.sun.com/javase/6/docs/api/java/text/Collator.html. –

+0

sí. Tenga en cuenta que es posible que desee excluir 'ü, å, ä' de la tabla de casos especiales anterior si desea que se vuelvan acentuados. Esas expansiones diphtong era lo que quería de mi POV (degradando más correctamente mi lenguaje); existen desafortunadas excepciones en otros idiomas para todas esas cosas (español, por ejemplo). – u0b34a0f6ae

Respuesta

5

Puede utilizar thisstrip_accents función para quitar los acentos:

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

Esto deja caracteres no controlados en la cadena, en lugar de despojarlos, lo cual es bueno. 'Dźwięk -> Dzwiek' pero 'Wyłącz -> Wyłacz'; entonces puedo agregar casos especiales además de eso – u0b34a0f6ae

+0

Acepto esta respuesta ya que el filtrado de la normalización va casi por completo. Verifique la pregunta actualizada sobre lo que uso. – u0b34a0f6ae

+1

Sin embargo, tenga en cuenta que desea filtrar el NF ** K ** D; esto es una descomposición sin equivalencia estricta, por ejemplo, los caracteres subíndices se convierten en números simples. – u0b34a0f6ae

1

Una solución de propósito general (especialmente para la normalización de búsqueda y babosas electrógenos) es el módulo unidecode:

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

¡Es un puerto del módulo Text :: Unidecode para Perl. No está completo, pero traduce todos los caracteres latinos que pude encontrar, transcribe el cirílico, el chino, etc. al latín e incluso maneja correctamente los caracteres de ancho completo.

Es probablemente una buena idea para quitar simplemente todos los caracteres que no desea tener en la salida final o reemplazarlos con un relleno (por ejemplo "äßœ$" se unidecoded a "assoe$", así que sería bueno para despojar a la no alfanuméricos)Para los caracteres que será transcribir, pero no debería (por ejemplo, § =>SS y =>EU) que necesita para limpiar la entrada:

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() 

Esto reemplazaría todos los caracteres no alfanuméricos con un maniquí y el reemplazo luego translitera la cadena y conviértela en minúscula.

+0

Gracias por este consejo. Mi aplicación es multilingüe, y no estoy seguro de si quiero dar demasiada información al respecto. Mi solución actual tiene características diferentes pero interesantes, como el script japonés 'ヘ' coincidirá 'ペ' al igual que 'a' coincidirá 'ä'; el diacritic-stripping es más universal, aunque no he establecido si esto es útil o no (solo tengo comentarios de usuarios occidentales). – u0b34a0f6ae

+0

@ u0b34a0f6ae - ¿bajo qué circunstancias te gustaría que 'ペ' (pe) coincida con 'ヘ' (he) ?? – simon

4

Para mi solicitud, que ya ha dirigido esto en un comentario distinto: Quiero tener un Unicode resultado y dejar caracteres no controlados intactos.

En ese caso, la forma correcta de hacerlo es crear un objeto de compaginación UCA con su intensidad establecida para comparar únicamente con la resistencia primaria, lo que hace caso omiso por completo de los signos diacríticos.

Muestro cómo hacer esto usando Perl en this answer. El primer objeto de compaginación tiene la fuerza que necesita, mientras que el segundo considera los acentos para desempatar.

Observará que no se han dañado las cadenas en la realización de estas comparaciones: los datos originales no se han modificado.

Cuestiones relacionadas