2012-10-05 46 views
9

Estoy intentando utilizar el módulo re en Python 2.7.3 con texto Devnagari codificado en Unicode. He agregado from __future__ import unicode_literals a la parte superior de mi código para que todos los literales de cadenas sean objetos unicode.Python Unicode coincidencia de expresión regular que falla con algunos caracteres Unicode -bug o error?

Sin embargo, me encuentro con algunos problemas extraños con la correspondencia de expresiones regulares de Python. Por ejemplo, considere este nombre: "किशोरी". Este es un nombre (mal escrito), en hindi, ingresado por uno de mis usuarios. Cualquier lector Hindi reconocería esto como una palabra.

El siguiente devuelve un partido, como debe ser:

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

pero esto no significa:

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

Algunos espeleología reveló que sólo un personaje en esta cadena, carácter 0915 (क), se reconoce como perteneciente a la clase de caracteres \ w. Esto es incorrecto, ya que la base de datos de caracteres Unicode file on "derived core properties" enumera otros caracteres (no he marcado todos) en esta cadena como alfabéticos, como de hecho lo son.

¿Es esto solo un error en la implementación de Python? Podría evitar esto definiendo manualmente todos los caracteres alfanuméricos de Devnagari como un rango de caracteres, pero sería doloroso. ¿O estoy haciendo algo mal?

Respuesta

7

Es un bug in the re module y se fija en el 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 

La salida muestra que hay 6 puntos de código en "किशोरी", pero sólo 3 caracteres percibida por el usuario (clusters de grafema extendida). Sería incorrecto romper una palabra dentro de un personaje.Unicode Text Segmentation dice:

extremos de las palabras, límites de líneas y límites de la frase no debería ocurren dentro de un grupo grafema: en otras palabras, un grupo grafema debe ser una unidad atómica con respecto al proceso de de determinar estos otros límites.

aquí y aún más énfasis es mío

Un límite de palabra \b se define como una transición de \w a \W (o a la inversa) en the docs:

Nota que formalmente, \ b se define como el límite entre un carácter \ w y un \ W (o viceversa), o entre \ w y el comienzo/fin de la cadena, ...

Por lo tanto, o bien todos los puntos de código que forman un solo carácter son \w o son todos \W. En este caso "किशोरी" coincide con ^\w{6}$.


De the docs for \w in Python 2:

Si UNICODE se pone, esto coinciden con los caracteres [0-9_] más lo que se clasifica como alfanumérica en la base de datos de caracteres Unicode propiedades.

en Python 3:

Partidos caracteres Unicode de la palabra; este incluye la mayoría de los caracteres que pueden ser parte de una palabra en cualquier idioma, así como los números y el guión bajo .

Desde regex docs:

definición del carácter 'palabra' (issue #1693050):

La definición de un carácter 'palabra' se ha ampliado para Unicode. Ahora se ajusta a la especificación Unicode en http://www.unicode.org/reports/tr29/.Esto se aplica a \ w, \ W, \ b y \ B.

Según unicode.org U+093F (DEVANAGARI VOWEL SIGN I) es alnum y alfabético por lo regex también es correcto considerar que \w incluso si seguimos las definiciones que no se basan en límites de palabra.

+0

Sí, puede confirmar que el módulo regex funciona. La clase de caracteres [[: alnum:]] POSIX también funciona con el módulo regex. – ShankarG

+0

@ShankarG: 'perl' acepta:' echo किशोरी | perl -CS -ne'print si/^ \ w + $/''(asume utf-8 io). – jfs

+0

Cambié mi marca "aceptada" a esta respuesta, ya que esta es la respuesta correcta, de hecho es un error en el módulo re. – ShankarG

3

De Mapa de caracteres:

ि

U + 093F Devanāgarī SIGN vocal i

Propiedades de carácter general

En Unicode desde: 1.1 Unicode categoría: Marcos, espaciado Combinando

Por lo tanto, técnicamente hablando, esto no es una carta y no está incluido en \w incluso con re.UNICODE. Puede intentar usar regex con propiedades de caracteres Unicode en su lugar para incluir este tipo de caracteres.

+0

De acuerdo con la [lista a la que he vinculado en las propiedades del código derivado arriba] (http://www.unicode.org/Public/UNIDATA/DerivedCoreProperties.txt) 093F está de hecho clasificado como un carácter alfabético. No estoy seguro de lo que está pasando. En cualquier caso, estos deben reconocerse como tales: estos personajes nunca se sostienen por sí solos, son modificaciones de los caracteres existentes para indicar sonidos vocálicos particulares (en este caso, el personaje indica que el "ka" posterior debe pronunciarse como un " ki "). – ShankarG

+0

Si se rechazaran estos y otros caracteres similares como caracteres alfabéticos, ningún texto de Devnagari en ninguno de los idiomas que lo utilizan (hindi, bengalí, marathi, etc.) se reconocerá alguna vez como alfabético por naturaleza. – ShankarG

+0

"093E..0940; Alfabético # Mc [3] DEVANAGARI VOWEL SIGNAR AA ... DEVANAGARI VOWEL SIGN II" Mc. ** Mc **. –

2

me probaron los siguientes:

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

que muestra en mi 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 cosas es difícil de depurar porque lío lata copiar y pegar los datos y no sé hindi. Pero en algunos idiomas puede codificar caracteres de diferentes maneras en Unicode. ¿Es posible que tengas que normalizar tu cadena de alguna manera antes de hacer coincidir? Para mí, parece correcto que un signo de vocal no coincida con \w.

+0

Ver comentario sobre la respuesta de Ignacio a continuación. Sin embargo, ¿a qué te refieres con normalizar? Tal vez eso sería el truco. – ShankarG

+0

No recuerdo los detalles exactos de mi cabeza, pero hay personajes que existen por sí solos y también pueden ser una cominbación. Por ejemplo, el alemán 'ä'. Es un personaje único, pero hasta donde sé hay una posibilidad de codificarlo como un marcador 'a' +' por tener esos puntos sobre él'. Y hay una transformación entre ambas versiones. Lo siento, no tengo posibilidad de verificar los detalles en este momento. – Achim

Cuestiones relacionadas