2009-09-11 16 views
9

Ho un'applicazione che implementa la ricerca incrementale. Ho un catalogo di stringhe unicode da abbinare e abbinarle a una determinata stringa "chiave"; una stringa di catalogo è un "hit" se contiene tutti i caratteri nella chiave, nell'ordine, ed è classificata meglio se i caratteri chiave si raggruppano nella stringa del catalogo.Come implementare la corrispondenza delle stringhe Unicode piegando in python

In ogni caso, questo funziona bene e corrisponde esattamente unicode, in modo che "öst" corrisponderà "Öst Blocket" o "r öst" o "r ö d st en".

In ogni caso, ora voglio implementare la piegatura, poiché ci sono alcuni casi in cui non è utile distinguere tra un carattere di catalogo come "á" o "é" e il carattere chiave "a" o "e".

Ad esempio: "Ole" deve corrispondere "Olé"

Come faccio meglio attuare questo matcher unicode-pieghevole in Python? L'efficienza è importante dal momento che devo abbinare migliaia di stringhe di catalogo alla chiave breve data.

Non deve trasformarlo in ascii; infatti, la stringa di output dell'algoritmo potrebbe essere unicode. Lasciare un personaggio dentro è meglio che spogliarlo.


Non so quale risposta accettare, poiché uso un po 'di entrambi. Prendendo la decomposizione NKFD e rimuovendo i segni combinati va quasi il modo di finire, aggiungo solo alcune traslitterazioni personalizzate a questo. Ecco il modulo come appare ora: (Attenzione, contiene caratteri Unicode in linea, dal momento che è molto più bello di modificare in questo modo.)

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

(E, per l'abbinamento effettiva se che gli interessi a nessuno: costruisco piegato corde per tutto il mio catalogo in anticipo, e mettere le versioni piegati nella proprietà catalogo degli oggetti alias già disponibili)

+0

Questo è davvero interessante e sarà probabilmente estremamente utile per il completamento automatico dei nomi di persone, poiché la maggior parte delle persone non si preoccuperà di introdurre accenti durante la ricerca di nomi. Sto facendo ricerche su come fare qualcosa di simile in Java. Questo sembra gestire alcuni casi: http://java.sun.com/javase/6/docs/api/java/text/Collator.html. –

+0

sì. Nota che potresti voler omettere 'ü, å, ä, ö' dalla tabella dei casi speciali sopra se vuoi che diventino semplicemente spogliati di accento. Quelle espansioni diftang era quello che volevo dal mio POV (più correttamente degradando il mio linguaggio); ci sono sfortunate eccezioni in altre lingue a tutte queste cose (spagnolo ü per esempio). – u0b34a0f6ae

risposta

5

È possibile utilizzare thisstrip_accents funzione per rimuovere gli accenti:

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

Lascia i caratteri non gestiti nella stringa, invece di eliminarli, il che è positivo. 'Dźwięk -> Dzwiek' ma 'Wyłącz -> Wyłacz'; quindi posso aggiungere casi speciali sopra a quello – u0b34a0f6ae

+0

Accetto questa risposta poiché il filtraggio della normalizzazione va quasi fino in fondo. Controlla la domanda aggiornata per quello che io uso. – u0b34a0f6ae

+1

Tuttavia, si noti che si desidera filtrare NF ** K ** D; questa è una scomposizione senza un'equivalenza rigorosa, ad esempio i caratteri pedici vengono convertiti in numeri semplici. – u0b34a0f6ae

1

Una soluzione general purpose (in particolare per la ricerca di normalizzazione e lumache elettrogeni) è il modulo unidecode:

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

E ' una porta del modulo Text :: Unidecode per Perl. Non è completo, ma traduce tutti i caratteri di derivazione latina che ho trovato, traslitterato in cirillico, cinese, ecc. In latino e persino a gestire correttamente i caratteri a larghezza intera.

E 'probabilmente una buona idea per togliere semplicemente tutti i caratteri non si vuole avere nel risultato finale o sostituirli con un riempitivo (ad es "äßœ$" saranno unidecoded a "assoe$", così si potrebbe desiderare mettere a nudo la non-alfanumerici).Per i caratteri si traslitterare ma non dovrebbe (diciamo, § =>SS e =>EU) è necessario ripulire l'ingresso:

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

Questo dovrebbe sostituire tutti i caratteri non alfanumerici con una sostituzione manichino e quindi traslitterare la stringa e trasformarla in lettere minuscole.

+0

Grazie per questo suggerimento. La mia applicazione è multilingue, e non sono sicuro se voglio dare una mancia eccessiva nel rigoroso alias ASCII. La mia attuale soluzione ha caratteristiche diverse ma interessanti, come la scrittura giapponese 'ヘ' corrisponderà a 'ペ', proprio come 'a' corrisponderà 'ä'; la diacritic-stripping è più universale, anche se non ho stabilito se questo è utile o meno (ho solo feedback dagli utenti occidentali). – u0b34a0f6ae

+0

@ u0b34a0f6ae - sotto quale tipo di circostanze vorreste 'ペ' (pe) abbinare 'ヘ' (lui) ?? – simon

4

Per la mia domanda, ho già affrontato questo in un commento diverso: io voglio avere un unicode risultato e lascia intatto i caratteri non gestiti.

In tal caso, il modo corretto per fare ciò è creare un oggetto collector UCA con il suo set di forza da confrontare solo con la forza primaria, che quindi ignora completamente i segni diacritici.

Ho mostrato come farlo utilizzando Perl in this answer. Il primo oggetto collector è alla forza di cui hai bisogno, mentre il secondo considera gli accenti per il tie-break.

Si noterà che nessuna stringa è stata danneggiata nella realizzazione di questi confronti: i dati originali non sono stati modificati.

Problemi correlati