Вопрос

У меня есть что -то вроде

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

Я хочу достичь чего -то вроде

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

Но поскольку म берет 4 байта, в то время как बि берет 8 байтов, я не могу добраться до этого прямо. Итак, что можно сделать, чтобы достичь этого? В Python.

Это было полезно?

Решение

Алгоритм разделения текста на графемные кластеры приведен в Приложение Unicode 29, раздел 3.1. Я не собираюсь реализовать полный алгоритм для вас здесь, но я покажу вам, как справиться с деванагари, а затем вы сможете прочитать приложение для себя и посмотреть, что еще нужно реализовать.

А unicodedata модуль Содержит информацию, необходимую для обнаружения кластеров графем.

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

В Деванагари каждый графемный кластер состоит из первоначальной буквы, дополнительных пар вирамы (гласный убийца) и буквы, а также дополнительного гласного знака. В регулярных нотациях выражения, что было бы LETTER (VIRAMA LETTER)* VOWEL?. Анкет Вы можете сказать, какой, посмотрев Категория Unicode Для каждой точки кода:

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

Буквы - это категория Lo (Письмо, другие), Гласные знаки являются категорией Mc (Марк, расстояние, объединяющее), вирус - это категория Mn (Марк, невыполнение) и пространства - это категория Zs (Сепаратор, пространство).

Итак, вот грубый подход к разделению кластеров графем:

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

Другие советы

Итак, вы хотите достичь чего -то подобного

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

Мой совет - отказаться от идеи, что индексация строки соответствует символам, которые вы видите на экране. Деванагари, а также несколько других сценариев, плохо играют с программистами, которые выросли с латинскими персонажами. Я предлагаю прочитать стандартную главу 9 Unicode (глава 9 (доступна здесь).

Похоже, что вы пытаетесь сделать, - это разбить строку на графные кластеры. Индексация строки сама по себе не позволит вам это сделать. Hangul - это еще один сценарий, который плохо играет с индексацией струн, хотя с комбинированными персонажами, даже чем -то, столь же знакомым, как испанский, вызовет проблемы.

Вам понадобится внешняя библиотека, такая как ICU для достижения этого (если у вас нет много свободного времени). ICU имеет привязки 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

Обратите внимание, как некоторые из этих «символов» (кластеры графем) имеют длину 2, а некоторые имеют длину 1. Вот почему индексация строки проблематична: если я хочу получить графический кластер #69450 из текстового файла, тогда мне нужно линейно сканировать через весь файл и подсчитывайте. Итак, ваши варианты:

  • Создайте индекс (вроде сумасшедшего ...)
  • Просто поймите, что вы не можете сломать на каждой границе персонажа. Объект итератора Break способен продвигаться как вперед, так и назад, поэтому, если вам нужно извлечь первые 140 символов строки, то вы посмотрите на индекс 140 и итерацию назад К предыдущему разрыву графем кластер, таким образом, вы не получаете забавный текст. (Еще лучше, вы можете использовать слов перерыв Итератор для соответствующей локали.) Преимущество использования этого уровня абстракции (итераторы символов и тому подобное) заключается в том, что он больше не имеет значения, какой кодирование вы используете: вы можете использовать UTF-8, UTF-16, UTF-32 и все это Просто работает. Ну, в основном работает.

Вы можете достичь этого с помощью Простая регуляция Для любого двигателя, который поддерживает \X

Демо

К сожалению, Python's re не поддерживается x Grapheme Match.

К счастью, предлагаемая замена, регулярно, поддерживает \X:

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

Есть библиотека с чистым питоном под названием uniseg который предоставляет ряд утилит, включая итератор кластера Grapheme, который предоставляет описанное вами поведение:

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

मे
रो

ना
म

हो

Он утверждает, что реализует полный алгоритм сегментации текста Unicode, описанный в http://www.unicode.org/reports/tr29/tr29-21.html.

Индийские и не латинские сценарии, такие как Hangul, обычно не следуют идее сопоставления строковых индексов с кодовыми точками. Как правило, это боль, работающая с индийскими сценариями. Большинство персонажей - два байта с некоторыми редкими, простирающимися на три. С дравидийцем это не определенный порядок. Увидеть Спецификация Unicode Больше подробностей.

Тем не менее, проверьте здесь Для некоторых идей о Unicode и Python с C ++.

Наконец, как сказал Дитрих, вы можете захотеть проверить Отделение интенсивной терапии слишком. Он имеет привязки, доступные для C/C ++ и Java через ICU4C и ICU4J соответственно. Есть какая -то кривая обучения, поэтому я предлагаю вам отложить в сторону немного много времени для этого. :)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top