私は次のようなものを持っています
a = "बिक्रम मेरो नाम हो"
私は次のようなことを達成したい
a[0] = बि
a[1] = क्र
a[3] = म
しかし、मは4バイトかかりますが、बिは8バイトかかるので、私はそれをまっすぐに行うことができません。では、それを達成するために何ができるでしょうか?Pythonで。
私は次のようなものを持っています
a = "बिक्रम मेरो नाम हो"
私は次のようなことを達成したい
a[0] = बि
a[1] = क्र
a[3] = म
しかし、मは4バイトかかりますが、बिは8バイトかかるので、私はそれをまっすぐに行うことができません。では、それを達成するために何ができるでしょうか?Pythonで。
テキストを書記素クラスターに分割するためのアルゴリズムは、UnicodeAnnex29のセクション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] = म
私のアドバイスは、文字列のインデックスが画面に表示される文字に対応しているという考えを捨てることです。デーバナーガリーや他のいくつかのスクリプトは、ラテン文字で育ったプログラマーにはうまく機能しません。Unicode標準の第9章(ここで入手可能)を読むことをお勧めします。
あなたがやろうとしているのは、文字列を書記素クラスターに分割することのようです。文字列のインデックス作成だけでは、これを行うことはできません。ハングルは、文字列のインデックス作成ではうまく機能しない別のスクリプトですが、文字を組み合わせると、スペイン語のように馴染みのあるものでも問題が発生します。
これを実現するには、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を取得する場合は、線形スキャンする必要があります。ファイル全体を調べてカウントします。したがって、オプションは次のとおりです。
これは、サポートする任意のエンジンの単純な正規表現で実現できます。\X
残念ながら、Pythonのreは\X書記素の一致をサポートしていません。
幸いなことに、提案された代替品である正規表現は、以下をサポートし\X
ます。
>>> a = "बिक्रम मेरो नाम हो"
>>> regex.findall(r'\X', a)
['बि', 'क्', 'र', 'म', ' ', 'मे', 'रो', ' ', 'ना', 'म', ' ', 'हो']
ハングルのようなインド語および非ラテン文字は、通常、文字列インデックスをコードポイントに一致させるという考えには従いません。一般に、インド語のスクリプトを使用するのは面倒です。ほとんどの文字は2バイトですが、まれな文字は3バイトに拡張されます。ドラヴィダ人では、それは明確な順序ではありません。詳細については、Unicode仕様を参照してください。
そうは言っても、 C++を使用したUnicodeとPythonに関するいくつかのアイデアについてはこちらを確認してください。
最後に、ディートリッヒが言ったように、 ICUもチェックしたいかもしれません。それぞれicu4cとicu4jを介してC/C++とjavaで使用できるバインディングがあります。いくつかの学習曲線が関係しているので、それのためにいくらかの時間を取っておくことをお勧めします。:)
uniseg
あなたが説明した振る舞いを提供するgraphemeクラスターイテレータを含む多くのユーティリティを提供すると呼ばれる純粋なPythonライブラリがあります:
>>> a = u"बिक्रम मेरो नाम हो"
>>> from uniseg.graphemecluster import grapheme_clusters
>>> for i in grapheme_clusters(a): print(i)
...
बि
क्
र
म
मे
रो
ना
म
हो
http://www.unicode.org/reports/tr29/tr29-21.htmlで説明されている完全なUnicodeテキストセグメンテーションアルゴリズムを実装すると主張しています。