结合天城文字符

时间:2011-07-24 06:26:22

标签: python unicode indic devanagari

我有类似

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

我希望实现像

这样的东西
a[0] = बि
a[1] = क्र
a[3] = म

但由于म占用4个字节,而बि需要8个字节,所以我无法直接进行。 那么可以做些什么呢?在Python中。

6 个答案:

答案 0 :(得分:20)

将文本拆分为字形集群的算法在Unicode Annex 29第3.1节中给出。我不打算在这里为你实现完整的算法,但是我将大致向你展示如何处理梵文的情况,然后你可以自己阅读附件,看看你还需要实现什么。

unicodedata module包含检测字形集群所需的信息。

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

在梵文中,每个字形簇都包含一个首字母,可选的一对virama(元音杀手)和字母,以及一个可选的元音符号。在正则表达式表示法中LETTER (VIRAMA LETTER)* VOWEL?。您可以通过查找每个代码点的Unicode category来确定哪个是:

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

答案 1 :(得分:14)

所以,你想要实现这样的目标

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

我的建议是放弃字符串索引对应于您在屏幕上看到的字符的想法。梵文和其他几个剧本在与拉丁字符一起长大的程序员中表现不佳。我建议阅读Unicode标准第9章(available here)。

看起来你要做的就是将字符串分成字形簇。字符串索引本身不会让你这样做。韩文是另一个使用字符串索引编写效果不佳的脚本,虽然使用组合字符,即使像西班牙语那样熟悉也会导致问题。

您将需要一个外部库(如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,它们都可以使用。好吧,大部分都有效。

答案 2 :(得分:2)

对于支持\X

的任何引擎,您都可以使用simple regex来实现此目的

Demo

不幸的是,Python重新does not support \ X字形匹配。

幸运的是,建议的替换regex确实支持\X

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

答案 3 :(得分:1)

有一个名为uniseg的纯Python库,它提供了许多实用程序,包括一个grapheme集群迭代器,它提供了你描述的行为:

>>> 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文本分段算法。

答案 4 :(得分:1)

语法

让我们非常快地讲解语法:The Devanagari Block作为开发人员,您需要关注两个字符类:

  • 符号:这是一个会影响以前出现的字符的字符。例如,此字符:。浅色圆圈指示要放置在其上的字符中心的位置。
  • 字母/元音/其他:这是一个可能会受到符号影响的字符。例如,此字符:

的组合结果:क्。但是组合可以扩展,因此क्षति实际上将变成क्षति(在这种情况下,我们将第一个字符右旋转90度,修改一些时尚元素,然后附加在第二个字符的左侧)。

我在这里的答案不是要解决这些无限(且非常漂亮)的情况,而不仅仅是解决单个字母簇和/或带有影响符号的单个字母簇的问题。如果我们正在考虑“这个梵文串的字符是什么?” ,那么这是正确的方法,否则,字母的任何组合都会形成具有唯一长度的唯一字符,然后是大多数字母系统相关的概念和算法将失败。

例如,一个符号词就是...

(letter) (letter) (sign) (sign) (letter) (sign)

在这种情况下,您需要结果...

[
    0=>(letter),
    1=>(letter) (sign) (sign),
    2=>(letter) (sign),
]

代码

那么逻辑并不算太糟糕,只需创建一个foreach循环即可。

我了解这是下面的JavaScript代码,但将应用相同的原理。设置sign类型...

function getEndWordGroupings() {return {'2304':true,'2305':true,'2306':true,'2307':true,'2362':true,'2363':true,'2364':true,'2365':true,'2366':true,'2367':true,'2368':true,'2369':true,'2370':true,'2371':true,'2372':true,'2373':true,'2374':true,'2375':true,'2376':true,'2377':true,'2378':true,'2379':true,'2380':true,'2381':true,'2382':true,'2383':true,'2385':true,'2386':true,'2389':true,'2390':true,'2391':true,'2402':true,'2403':true,'2416':true,'2417':true,};}

并将字符串转换为字符...

function stringToChars(args) {
    var word = args.word;
    var chars = [];
    
    var endings = getEndWordGroupings();
    
    var incluster = false;
    var cluster = '';
    
    var whitespace = new RegExp("\\s+");
    
    for(var i = word.length - 1; i >= 0; i--) {
        var character = word.charAt(i);
        var charactercode = word.charCodeAt(i);
        
        if(incluster) {
            if(whitespace.test(character)) {
                incluster = false;
                chars.push(cluster);
                cluster = '';
            } else if(endings[charactercode]) {
                chars.push(cluster);
                cluster = character;
            } else {
                incluster = false;
                cluster = character + cluster;
                chars.push(cluster);
                cluster = '';
            }
        } else if(endings[charactercode]) {
            incluster = true;
            cluster = character;
        } else if(whitespace.test(character)) {
            incluster = false;
            chars.push(cluster);
            cluster = '';
        } else {
            chars.push(character);
        }
    }
    
    if(cluster.length > 0) {
        chars.push(cluster);
    }
    
    return chars.reverse();
}

console.log(stringToChars({'word':'क्षऀति'}));</script>

结果

输出:

["क्", "षऀ", "ति"]

如果我使用普通解析,则输出应该是

["क", "्", "ष", "त", "ि"]

提示:看到上面的两个标志,上面有一个小圆圈?浅色圆圈指示该符号影响的字符的位置。回顾转换后的翻译,很容易看到字母是如何组合成新字符的。干净!

答案 5 :(得分:0)

像Hangul这样的印度语和非拉丁语脚本通常不遵循将字符串索引与代码点匹配的想法。使用印度语脚本通常很痛苦。大多数字符是两个字节,一些稀有字符扩展为三个字节。使用Dravidian,它没有定义的顺序。有关详细信息,请参阅Unicode specification

也就是说,使用C ++检查here有关unicode和python的一些想法。

最后,正如Dietrich所述,您可能也想查看ICU。它分别通过icu4c和icu4j为C / C ++和java提供绑定。有一些学习曲线,所以我建议你留出一些的时间。 :)