我有类似的东西

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

我想取得像

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

但是,由于म采用4个字节,而बि取8个字节,我无法直奔这一字节。那么如何实现这一目标呢?在Python。

有帮助吗?

解决方案

用于将文本分成码头簇的算法在 Unicode附件29, ,第3.1节。我不会在这里为您实施完整的算法,但是我会向您展示如何处理Devanagari的情况,然后您可以自己阅读附件,看看需要实施什么。

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

在Devanagari中,每个素集簇都由首字母,可选的Virama(元音杀手)和字母和一个可选的元音符号组成。在正则表达符号中 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 (马克,间距组合),virama是类别 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] = म

我的建议是抛弃字符串索引与您在屏幕上看到的字符相对应的想法。 Devanagari以及其他几个脚本都不适合与拉丁角色一起长大的程序员。我建议阅读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。这就是为什么字符串索引有问题的原因:如果我想从文本文件中获取gruseme cluster#69450,那么我必须线性扫描通过整个文件和计数。因此,您的选择是:

  • 建立一个索引(有点疯狂...)
  • 只是意识到您不能在每个角色边界上打破。断裂迭代器对象能够向前和向后进行,因此,如果您需要提取字符串的前140个字符,则可以查看索引140并迭代 向后 在上一个素式集群中断,您不会最终得到有趣的文本。 (更好的是,您可以使用 单词中断 适当语言环境的迭代器。)使用此级别的抽象(字符迭代器之类)的好处是,它不再重要的是您使用的编码:您可以使用utf-8,utf-16,utf-32,所有这些都可以只是起作用。好吧,主要是有效的。

您可以通过 简单的正则 对于任何支持的引擎 \X

演示

不幸的是,Python的RE 不支持 x grapheme匹配。

幸运的是,拟议的替代者, 正则, ,确实支持 \X:

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

有一个纯净的python图书馆叫 uniseg 它提供了许多实用程序,包括素集团迭代器,该实用程序提供了您描述的行为:

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

指示器和非拉丁文脚本(例如Hangul)通常不会遵循将字符串索引与代码点匹配的想法。通常,这是一种痛苦的脚本。大多数字符是两个字节,其中一些罕见的字符延伸成三个字符。使用Dravidian,这不是定义的顺序。看到 Unicode规范 更多细节。

就是说,检查 这里 有关Unicode和C ++ Python的一些想法。

最后,如所说 迪特里希, ,您可能想结帐 ICU 也。它分别通过ICU4C和ICU4J可用于C/C ++和Java。涉及一些学习曲线,所以我建议您准备 一些 它的时间为此。 :)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top