2009-12-02 10 views
48

Perl e alcuni altri motori regex correnti supportano le proprietà Unicode, come la categoria, in un'espressione regolare. Per esempio. in Perl è possibile utilizzare \p{Ll} per abbinare una lettera minuscola arbitraria o p{Zs} per qualsiasi separatore di spazi. Non vedo il supporto per questo nelle linee 2.xe 3.x di Python (con i dovuti rimpianti). Qualcuno è a conoscenza di una buona strategia per ottenere un effetto simile? Le soluzioni homegrown sono benvenute.regex Python corrispondente alle proprietà Unicode

+10

In realtà, Perl supporta ** tutte le proprietà ** Unicode, non solo le categorie generali. Gli esempi includono '\ p {Block = Greco}, \ p {Script = Armeno}, \ p {Generale_Categoria = Uppercase_Letter}, \ p {Spazio Bianco}, \ p {Alfabetico}, \ p {Matematica}, \ p {Bidi_Class = Right_to_Left}, \ p {Word_Break = A_Letter }, \ p {Numeric_Value = 10}, \ p {Hangul_Syllable_Type = Leading_Jamo}, \ p {Sentence_Break = SContinue}, 'e circa 1,000 in più. Solo le regex di Perl e ICU si preoccupano di coprire l'intero complemento delle proprietà Unicode. Tutti gli altri coprono pochi minuscoli, in genere nemmeno abbastanza per il minimo lavoro Unicode. – tchrist

risposta

22

Hai provato Ponyguruma, un collegamento Python al motore di espressioni regolari Oniguruma? In quel motore puoi semplicemente dire \p{Armenian} per abbinare i caratteri armeni. \p{Ll} o \p{Zs} funzionano anche.

+2

questo modulo non ha la stessa API del modulo re Python –

+7

L'ultimo commit al modulo Ponyguruma era apparentemente 2010 (http://dev.pocoo.org/hg/sandbox/ponyguruma) mentre il modulo regex di Python su PyPI è attivamente sviluppato: http://pypi.python.org/pypi/regex – RichVel

4

Hai ragione che le classi di proprietà Unicode non sono supportate dal parser regex Python.

Se si voleva fare un bel trucco, che sarebbe generalmente utile, è possibile creare un preprocessore che analizza una stringa per tali token di classe (\p{M} o altro) e li sostituisce con i set di caratteri corrispondenti, in modo che, per Ad esempio, \p{M} diventerebbe [\u0300–\u036F\u1DC0–\u1DFF\u20D0–\u20FF\uFE20–\uFE2F] e \P{M} diventerebbe [^\u0300–\u036F\u1DC0–\u1DFF\u20D0–\u20FF\uFE20–\uFE2F].

La gente ti ringrazierebbe. :)

+1

Giusto, mi è venuto in mente di creare classi di personaggi. Ma con circa 40 categorie si finisce per produrre 80 classi, e questo non conta gli script unicode, i blocchi, i piani e quant'altro.Potrebbe valere un piccolo progetto open source, ma ancora un incubo di manutenzione. Ho appena scoperto che re.VERBOSE non si applica alle classi di caratteri, quindi non ci sono commenti qui o spazio bianco per facilitare la leggibilità ... – ThomasH

2

Si noti che mentre \p{Ll} non ha equivalenti nelle espressioni regolari Python, \p{Zs} deve essere coperto da '(?u)\s'. Il (?u), come dicono i documenti, "Crea \ w, \ W, \ b, \ B, \ d, \ D, \ s e \ S dipende dal database delle proprietà dei caratteri Unicode." E \s indica qualsiasi carattere di spaziatura.

+0

Hai ragione. Il problema è che '(? U) \ s' è più grande di '\ p {Zs}', incluso per es. nuova linea. Quindi se vuoi davvero abbinare solo i separatori di spazio, il primo è troppo generoso. – ThomasH

+3

@ThomasH: per ottenere "lo spazio eccetto che non è nuovo" è possibile utilizzare la classe di caratteri doppia negata: '(? U) [^ \ S \ n]' – bukzor

6

È possibile utilizzare faticosamente unicodedata su ogni personaggio:

import unicodedata 

def strip_accents(x): 
    return u''.join(c for c in unicodedata.normalize('NFD', x) if unicodedata.category(c) != 'Mn') 
+0

Grazie. Sebbene al di fuori delle espressioni regolari, questa potrebbe essere un'alternativa valida per alcuni casi. – ThomasH

+0

Sembra che il modulo Python 'unicodedata' non contenga informazioni su es. lo script o il blocco Unicode di un personaggio. Vedi anche https://stackoverflow.com/questions/48058402/unicode-table-information-about-a-character-in-python/48060112#48060112 – tripleee

52

Il modulo regex (in alternativa al modulo standard re) supporta le proprietà codepoint Unicode con la sintassi \p{}.

+1

Non sai quanto sia completo il supporto '' \ p {} '', ma questo modulo è attivamente sviluppato e dovrebbe alla fine sostituire il modulo '' re'' integrato: vedi http://pypi.python.org/pypi/regex – RichVel

+4

+1: 'regex' è una sostituzione drop-in per stdlib modulo 're'. Se sai come usare 're'; puoi immediatamente usare 'regex'. 'importa regex as re' e hai il supporto per la sintassi' \ p {} '. Ecco un [esempio su come rimuovere tutte le punteggiature in una stringa usando '\ p {P}'] (http://stackoverflow.com/a/11066687) – jfs

5

Parlando di soluzioni homegrown, qualche tempo fa ho scritto un piccolo program di fare proprio questo - convertire una categoria unicode scritto come \p{...} in un intervallo di valori, estratto dal unicode specification (v.5.0.0). Sono supportate solo le categorie (es .: L, Zs) ed è limitato al BMP. Sto postando qui nel caso qualcuno lo trovi utile (anche se Oniguruma sembra davvero un'opzione migliore).

Esempio utilizzo:

>>> from unicode_hack import regex 
>>> pattern = regex(r'^\\p{Lu}(\\p{L}|\\p{N}|_)*') 
>>> print pattern.match(u'疂_1+2').group(0) 
疂_1 
>>> 

Ecco il source. C'è anche un JavaScript version, usando gli stessi dati.

+1

Bello, anche se stai usando letterali fatti a mano per le gamme nel codice. Sarebbe bello avere quei letterali generati da qualche forma testuale delle specifiche. O da unicodedata (http://docs.python.org/library/unicodedata.html#module-unicodedata). Potresti probabilmente eseguire tutti i punti di codice Unicode validi ed eseguirli attraverso unicodedata.category(), e usare l'output per compilare la mappa ... – ThomasH

+0

Grazie per il suggerimento, potrei implementarlo un giorno. Il codice sopra è stato creato per JavaScript in primo luogo (per il quale c'erano poche alternative ragionevoli al momento), quindi portato su Python. Ho fatto alcune regex sulle specifiche e ho finito con una sceneggiatura usa e getta, ma sono d'accordo che una procedura ripetibile sarebbe stata migliore, quindi posso tenerlo aggiornato. – mgibsonbr

+0

Ho violato una funzione rapida che crea dinamicamente la mappa (con solo elenchi di caratteri come valori): def unicats (maxu): m = defaultdict (elenco) per i in range (maxu): provare : cat = unicodedata.category (unichr (i)) eccezione: cat = Nessuno se gatto: m [cat] .Append (i) ritorno m m = unicats (10FFFF) essere consapevoli del fatto che alcune categorie diventano davvero grande (es. len (m ['Cn']) == 873882). – ThomasH

Problemi correlati