Question

J'ai quelque chose comme

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

Je veux obtenir quelque chose comme

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

mais comme ? prend 4 octets tandis que ?? prend 8 octets Je ne suis pas en mesure d'arriver à ce droit. Alors, que pourrait-on faire pour y parvenir? En Python.

Était-ce utile?

La solution

L'algorithme de texte se diviser en groupes de graphèmes est donnée dans Unicode Annexe 29 , section 3.1. Je ne vais pas mettre en œuvre l'algorithme complet pour vous, mais je vais vous montrer à peu près comment gérer le cas de Devanagari, et vous pouvez lire l'annexe pour vous-même et voir ce que vous devez mettre en œuvre.

Le unicodedata contient les informations nécessaires pour détecter les grappes de graphèmes.

>>> 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']

Devanagari, chaque groupe graphème se compose d'une première lettre, les paires optionnelles de virama (killer voyelle) et de la lettre, et un signe de voyelle en option. Dans la notation d'expression régulière qui serait LETTER (VIRAMA LETTER)* VOWEL?. Vous pouvez dire qui est qui en regardant la catégorie Unicode pour chaque point de code:

>>> [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']

Les lettres sont la catégorie Lo (Lettre, Autre), les signes de voyelles sont la catégorie Mc (Mark, espacement La combinaison), virama est la catégorie Mn (Mark, sans chasse) et les espaces sont la catégorie Zs (séparateur, Espace).

Alors, voici une approche grossière de diviser les grappes de graphèmes:

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))
['बि', 'क्र', 'म', ' ', 'मे', 'रो', ' ', 'ना', 'म', ' ', 'हो']

Autres conseils

, vous voulez obtenir quelque chose comme ceci

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

Mon conseil est de fossé l'idée qui correspond à l'indexation des chaînes aux caractères que vous voyez sur l'écran. Devanagari, ainsi que plusieurs autres scripts, ne jouent pas bien avec les programmeurs qui ont grandi avec des caractères latins. Je vous suggère de lire le chapitre 9 norme Unicode ( ici ).

Il ressemble à ce que vous essayez de faire est de briser une chaîne en groupes de graphèmes. l'indexation des chaînes par lui-même ne vous laissera pas faire. Hangul est un autre script qui joue mal avec l'indexation des chaînes, mais avec la combinaison de caractères, même quelque chose d'aussi familier que l'espagnol causera des problèmes.

Vous aurez besoin d'une bibliothèque externe comme unité de soins intensifs pour y parvenir (sauf si vous avez beaucoup de temps libre). ICU a des liens avec Python.

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

Notez comment certains de ces « personnages » (clusters de graphèmes) ont une longueur de 2, et certains ont une longueur 1. C'est pourquoi l'indexation des chaînes est problématique: si je veux obtenir graphème groupe # 69450 à partir d'un fichier texte, puis je pour scanner linéaire à travers le fichier entier et compter. Ainsi, vos options sont:

  • Construire un index (un peu fou ...)
  • Il faut juste réaliser que vous ne pouvez pas briser sur toutes les limites de caractère. La rupture objet iterator est capable d'aller en avant et en arrière, donc si vous avez besoin d'extraire les 140 premiers caractères d'une chaîne, vous regardez à l'index 140 et itérer en arrière à la rupture de cluster précédente graphème, de cette façon vous ne finissent pas avec le texte drôle. (. Mieux encore, vous pouvez utiliser un break mot iterator pour les paramètres régionaux appropriés) L'avantage d'utiliser ce niveau d'abstraction (caractère itérateurs, etc.) est qu'il ne compte plus que vous codant pour utiliser: vous pouvez utiliser UTF-8, UTF-16, UTF-32 et tout fonctionne tout simplement. Eh bien, la plupart du temps fonctionne.

Vous pouvez y parvenir avec un simple, regex pour tout moteur qui supports \X

Démo

Malheureusement, re Python ne supporte pas le \ X graphème correspondance.

Heureusement, le remplacement proposé, regex , pris en charge par \X:

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

Il y a une bibliothèque pure Python appelé uniseg qui fournit un certain nombre de services publics, y compris un groupe de graphème iterator qui fournit le comportement que vous avez décrit:

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

मे
रो

ना
म

हो

Il prétend mettre en œuvre l'ensemble algorithme de segmentation de texte Unicode décrit dans http: // www.unicode.org/reports/tr29/tr29-21.html .

Indic et scripts non latins comme Hangul ne suivent généralement pas l'idée d'indices de chaîne correspondant à des points de code. Il est généralement une douleur à travailler avec des scripts Indic. La plupart des personnages sont deux octets avec quelques plus rares s'étendant en trois. Avec dravidienne, il est sans ordre défini. Voir Unicode spécifications pour plus de détails.

Cela dit, chèque ici quelques idées sur unicode et python avec C ++.

Enfin, comme l'a dit Dietrich , vous pouvez consultez soins intensifs aussi. Il a des liaisons disponibles pour C / C ++ et java via icu4c et icu4j respectivement. Il y a une courbe d'apprentissage, alors je vous suggère de mettre de côté certains charges de temps pour elle. :)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top