Pregunta

Tengo algo como

a = "बिक्रम मेरो नाम हो"

Quiero lograr algo como

a[0] = बि
a[1] = क्र
a[3] = म

Pero como म toma 4 bytes mientras बि toma 8 bytes, no puedo llegar a esa recta. Entonces, ¿qué se podría hacer para lograr eso? En Python.

¿Fue útil?

Solución

El algoritmo para dividir el texto en clústeres de grafeme se da en Unicode Anexo 29, Sección 3.1. No voy a implementar el algoritmo completo para usted aquí, pero le mostraré más o menos cómo manejar el caso de Devanagari, y luego puede leer el anexo para usted y ver qué más necesita implementar.

los unicodedata módulo Contiene la información que necesita para detectar los grupos de grafeme.

>>> import unicodedata
>>> a = "बिक्रम मेरो नाम हो"
>>> [unicodedata.name(c) for c in a]
['DEVANAGARI LETTER BA', 'DEVANAGARI VOWEL SIGN I', 'DEVANAGARI LETTER KA', 
 'DEVANAGARI SIGN VIRAMA', 'DEVANAGARI LETTER RA', 'DEVANAGARI LETTER MA',
 'SPACE', 'DEVANAGARI LETTER MA', 'DEVANAGARI VOWEL SIGN E',
 'DEVANAGARI LETTER RA', 'DEVANAGARI VOWEL SIGN O', 'SPACE',
 'DEVANAGARI LETTER NA', 'DEVANAGARI VOWEL SIGN AA', 'DEVANAGARI LETTER MA',
 'SPACE', 'DEVANAGARI LETTER HA', 'DEVANAGARI VOWEL SIGN O']

En Devanagari, cada clúster de grafema consiste en una letra inicial, pares opcionales de Virama (asesino de vocales) y letra, y un signo vocal opcional. En notación de expresión regular que sería LETTER (VIRAMA LETTER)* VOWEL?. Puedes saber cuál es cuál al buscar el Categoría Unicode Para cada punto de código:

>>> [unicodedata.category(c) for c in a]
['Lo', 'Mc', 'Lo', 'Mn', 'Lo', 'Lo', 'Zs', 'Lo', 'Mn', 'Lo', 'Mc', 'Zs',
 'Lo', 'Mc', 'Lo', 'Zs', 'Lo', 'Mc']

Las cartas son categoría Lo (Carta, otra), los signos vocales son categorías Mc (Marcos, combinación de espacios), Virama es categoría Mn (Marcos, no respuestas) y los espacios son categorías Zs (Separador, espacio).

Así que aquí hay un enfoque aproximado para dividir los clústeres de grafeme:

def splitclusters(s):
    """Generate the grapheme clusters for the string s. (Not the full
    Unicode text segmentation algorithm, but probably good enough for
    Devanagari.)

    """
    virama = u'\N{DEVANAGARI SIGN VIRAMA}'
    cluster = u''
    last = None
    for c in s:
        cat = unicodedata.category(c)[0]
        if cat == 'M' or cat == 'L' and last == virama:
            cluster += c
        else:
            if cluster:
                yield cluster
            cluster = c
        last = c
    if cluster:
        yield cluster

>>> list(splitclusters(a))
['बि', 'क्र', 'म', ' ', 'मे', 'रो', ' ', 'ना', 'म', ' ', 'हो']

Otros consejos

Entonces, quieres lograr algo como esto

a[0] = बि a[1] = क्र a[3] = म

Mi consejo es deshacerse de la idea de que la indexación de cadenas corresponde a los caracteres que ve en la pantalla. Devanagari, así como varios otros guiones, no juegan bien con los programadores que crecieron con personajes latinos. Sugiero leer el Capítulo 9 estándar Unicode (disponible aquí).

Parece que lo que está tratando de hacer es dividir una cadena en los clústeres de Grapheme. La indexación de cadenas por sí misma no le permitirá hacer esto. Hangul es otro guión que juega mal con la indexación de cuerdas, aunque con los personajes combinados, incluso algo tan familiar como el español causará problemas.

Necesitará una biblioteca externa como la UCI para lograr esto (a menos que tenga mucho tiempo libre). La UCI tiene enlaces de pitón.

>>> a = u"बिक्रम मेरो नाम हो"
>>> import icu
    # Note: This next line took a lot of guesswork.  The C, C++, and Java
    # interfaces have better documentation.
>>> b = icu.BreakIterator.createCharacterInstance(icu.Locale())
>>> b.setText(a)
>>> i = 0
>>> for j in b:
...     s = a[i:j]
...     print '|', s, len(s)
...     i = j
... 
| बि 2
| क् 2
| र 1
| म 1
|   1
| मे 2
| रो 2
|   1
| ना 2
| म 1
|   1
| हो 2

Tenga en cuenta cómo algunos de estos "caracteres" (clústeres de grafeme) tienen longitud 2, y otros tienen una longitud 1. Es por eso que la indexación de cadenas es problemática: si quiero obtener el clúster de Grapheme #69450 de un archivo de texto, entonces tengo que escanear linealmente a través de todo el archivo y contar. Entonces tus opciones son:

  • Construir un índice (un poco loco ...)
  • Solo date cuenta de que no puedes romper en cada límite de personajes. El objeto Break Iterator es capaz de avanzar tanto hacia adelante como hacia atrás, por lo que si necesita extraer los primeros 140 caracteres de una cadena, entonces mira el índice 140 e itera hacia atrás Para la ruptura anterior del clúster Grapheme, de esa manera no terminas con un texto divertido. (Mejor aún, puedes usar un Palabra de palabras iterador para la localidad apropiada). El beneficio de usar este nivel de abstracción (iteradores de carácter y similares) es que ya no importa qué codificación usa: puede usar UTF-8, UTF-16, UTF-32 y todo Solo funciona. Bueno, en su mayoría funciona.

Puedes lograr esto con un Regex simple para cualquier motor que admita \X

Manifestación

Desafortunadamente, la RE de Python no soporta la coincidencia de grafema x.

Afortunadamente, el reemplazo propuesto, regex, es compatible \X:

>>> a = "बिक्रम मेरो नाम हो"
>>> regex.findall(r'\X', a)
['बि', 'क्', 'र', 'म', ' ', 'मे', 'रो', ' ', 'ना', 'म', ' ', 'हो']

Hay una biblioteca de pitón puro llamado uniseg que proporciona una serie de utilidades, incluido un iterador de clúster de Grapheme que proporciona el comportamiento que describió:

>>> a = u"बिक्रम मेरो नाम हो"
>>> from uniseg.graphemecluster import grapheme_clusters
>>> for i in grapheme_clusters(a): print(i)
... 
बि
क्
र
म

मे
रो

ना
म

हो

Afirma implementar el algoritmo completo de segmentación de texto Unicode descrito en http://www.unicode.org/reports/tr29/tr29-21.html.

Los scripts indic y no latinos como Hangul generalmente no siguen la idea de hacer coincidir los índices de cadena con los puntos de código. Generalmente es un dolor para trabajar con scripts indic. La mayoría de los personajes son dos bytes con algunos raros que se extienden en tres. Con Dravidian, no es un orden definido. Ver el Especificación unicode para más detalles.

Dicho esto, verifique aquí Para algunas ideas sobre Unicode y Python con C ++.

Finalmente, como dijo Dietrich, es posible que desee ver UCI también. Tiene enlaces disponibles para C/C ++ y Java a través de ICU4C e ICU4J respectivamente. Hay alguna curva de aprendizaje involucrada, así que le sugiero que dejes de lado alguno Mucho tiempo para ello. :)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top