2012-10-05 10 views
9

Sto tentando di utilizzare il modulo re in Python 2.7.3 con testo Devnagari codificato in Unicode. Ho aggiunto from __future__ import unicode_literals all'inizio del mio codice in modo che tutti i valori letterali delle stringhe siano oggetti unicode.Corrispondenza di espressioni regolari di unicode Python in mancanza di alcuni caratteri Unicode -bug o errore?

Tuttavia, sto riscontrando alcuni strani problemi con la corrispondenza regex di Python. Ad esempio, considera questo nome: "किशोरी". Questo è un nome (errato), in hindi, inserito da uno dei miei utenti. Qualsiasi lettore hindi lo riconoscerebbe come una parola.

Di seguito restituisce una partita, come si deve:

re.search("^[\w\s][\w\s]*","किशोरी",re.UNICODE)

ma questo non significa:

re.search("^[\w\s][\w\s]*$","किशोरी",re.UNICODE)

Alcuni speleologia ha rivelato che solo un personaggio in questa stringa, carattere 0915 (क), viene riconosciuto come rientrante nella classe di caratteri \ w. Questo non è corretto, poiché il database dei caratteri Unicode file on "derived core properties" elenca gli altri caratteri (non li ho controllati tutti) in questa stringa come alfabetici, come in effetti lo sono.

Questo è solo un bug nell'implementazione di Python? Potrei aggirare questo definendo manualmente tutti i caratteri alfanumerici Devnagari come un intervallo di caratteri, ma sarebbe doloroso. O sto facendo qualcosa di sbagliato?

risposta

7

Si tratta di un bug in the re module e viene risolto nel regex module:

# -*- coding: utf-8 -*- 
from __future__ import unicode_literals 
import unicodedata 
import re 
import regex # $ pip install regex 

word = "किशोरी" 


def test(re_): 
    assert re_.search("^\\w+$", word, flags=re_.UNICODE) 

print([unicodedata.category(cp) for cp in word]) 
print(" ".join(ch for ch in regex.findall("\\X", word))) 
assert all(regex.match("\\w$", c) for c in ["a", "\u093f", "\u0915"]) 

test(regex) 
test(re) # fails 

L'output mostra che vi sono 6 codepoints in "किशोरी", ma solo 3 caratteri percepita dall'utente (cluster grafema estesi). Sarebbe sbagliato rompere una parola all'interno di un personaggio.Unicode Text Segmentation dice:

confini Word, confini linee e limiti della frase non dovrebbe si verificano all'interno di un cluster grafema: in altre parole, un cluster grafema dovrebbe essere un'unità atomica rispetto al processo di determinare questi altri confini.

qui e ulteriore enfasi è mio

Un limite di parola \b è definita come una transizione da \w a \W (o al contrario) in the docs:

noti che formalmente, \ b è definito come il confine tra un \ w e un carattere \ W (o viceversa), o tra \ w e l'inizio/fine di la stringa, ...

Pertanto sia tutte Codepoints che formano un singolo carattere sono \w o sono tutti \W. In questo caso "किशोरी" corrisponde a ^\w{6}$.


Da the docs for \w in Python 2:

Se UNICODE è impostato, questo farà corrispondere i caratteri [0-9_] più tutto ciò che è classificato come alfanumerico in Unicode carattere proprietà del database.

in Python 3:

Partite caratteri alfanumerici Unicode; questo include la maggior parte dei caratteri che può essere parte di una parola in qualsiasi lingua, così come i numeri e il carattere di sottolineatura .

Da regex docs:

Definizione di carattere 'parola' (issue #1693050):

La definizione di un carattere 'parola' è stata ampliata per Unicode. Ora è conforme alle specifiche Unicode allo http://www.unicode.org/reports/tr29/.Questo vale per \ w, \ W, \ b e \ B.

Secondo unicode.org U+093F (DEVANAGARI VOWEL SIGN I) è alnum e alfabetica così regex è anche corretto considerarlo \w, anche se seguiamo le definizioni che non si basano su confini di parola.

+0

Sì, posso confermare che il modulo regex funziona. La classe di caratteri [[:: alnum:]] POSIX funziona anche con il modulo regex. – ShankarG

+0

@ShankarG: 'perl' è d'accordo:' echo किशोरी | perl -CS -ne'print se/^ \ w + $/''(presume utf-8 io). – jfs

+0

Cambiato il mio segno "accettato" in questa risposta, poiché questa è in effetti la risposta corretta - è effettivamente un bug nel modulo re. – ShankarG

3

Da Mappa Caratteri:

ि

U + 093F Carattere alfabeto devanagari ho

proprietà generali di carattere

In Unicode dal: 1.1 Unicode categoria: Mark, spaziatura Combinazione

Quindi, tecnicamente parlando questa non è una lettera e non rientra in \w anche con re.UNICODE. Puoi provare a utilizzare regex con le proprietà dei caratteri Unicode al fine di includere questi tipi di caratteri.

+0

In base alla [lista I collegata alle proprietà del codice derivato sopra] (http://www.unicode.org/Public/UNIDATA/DerivedCoreProperties.txt) 093F è effettivamente classificato come carattere alfabetico. Non sono sicuro di cosa sta succedendo. In ogni caso, questi devono essere riconosciuti come tali - questi personaggi non stanno mai in piedi da soli, sono modifiche di caratteri esistenti per indicare particolari suoni vocalici (in questo caso il carattere indica che il successivo "ka" dovrebbe essere pronunciato come un " ki "). – ShankarG

+0

Se si rifiutassero questi caratteri e caratteri simili come caratteri alfabetici, nessun testo Devnagari in nessuna delle lingue che lo usano (hindi, bengali, marathi, ecc.) Sarebbe mai riconosciuto come di natura alfabetica. – ShankarG

+0

"093E..0940; Alfabetico # Mc [3] SEGNO VOCALE DI DEVANAGARI AA ... SEGNO DI VOCE DI DEVANAGARI II" Mc. ** ** Mc. –

2

ho verificato le seguenti:

import unicodedata 
for c in "किशोरी": 
    print unicodedata.category(c) 
    print unicodedata.name(c) 

che mostra nel mio caso:

Lo 
DEVANAGARI LETTER KA 
Mc 
DEVANAGARI VOWEL SIGN I 
Lo 
DEVANAGARI LETTER SHA 
Mc 
DEVANAGARI VOWEL SIGN O 
Lo 
DEVANAGARI LETTER RA 
Mc 
DEVANAGARI VOWEL SIGN II 

Unicode roba è difficile da eseguire il debug a causa di copia e incolla può rovinare i dati e non so hindi. Ma in alcune lingue è possibile codificare i caratteri in modi diversi in Unicode. È possibile che tu debba normalizzare la stringa in qualche modo prima della corrispondenza? Per me sembra ok che un segno vocalico non corrisponda a \w.

+0

Vedere il commento sulla risposta di Ignacio qui sotto. Cosa intendi per normalizzazione, però? Forse sarebbe il trucco. – ShankarG

+0

Non ricordo i dettagli esatti dalla mia testa, ma esiste un personaggio che esiste da solo e può anche essere una dichiarazione. Ad esempio il tedesco 'ä'. È un singolo personaggio, ma per quanto ne so c'è la possibilità di codificarlo come 'a' +' per avere quei punti sopra di esso '. E c'è una trasformazione tra entrambe le versioni. Scusa, non ho la possibilità di controllare i dettagli al momento. – Achim

Problemi correlati