Unicode格式

Sav*_*esh 3 python python-unicode

我正在使用字符串格式.对于英语格式是格式整洁,但对于unicode字符,格式是随意的.谁能告诉我原因?例:

form = u'{:<15}{:<3}({})'
a = [
 u'?? ???????',
 u'?? ????????',
 u'?? ???????',
 u'?? ???? ',
 u'?? ???????…',
 u'?? ???? ',
 u'?? ????? ',
 u'?? ?????? ',
 u'? ?????? ',
 u'?? ?????????????? ??',]

 for i in range(0, 10):
     print form.format(a[i][:12], 1, 2)
Run Code Online (Sandbox Code Playgroud)

输出为 在此输入图像描述

s = [
 u'abcdef',
 u'akash',
 u'rohit',
 u'anubhav',
 u'bhargav',
 u'achut',
 u'punnet',
 u'tom',
 u'rach',
 u'kamal'
 ]
for i in range(0, 10):
     print form.format(s[i][:12], 1, 2)
Run Code Online (Sandbox Code Playgroud)

得到: 在此输入图像描述

Mar*_*ers 7

您正在打印Malayalam Unicode代码点,它使用大量元音符号来修改前面的字形.这些元音符号代码点本身不会形成新的字母,并且Malayalam在终端中不会像ASCII字母那样产生相同的常规输出宽度.

例如,在您的第一个字符串中以U + 0D38 MALAYALAM LETTER SAU + 0D3F MALAYALAM VOWEL SIGN I开头.第一个字母SA在屏幕上占据一个完整的位置,但第二个字符,即元素符号I,在SA之前,改变了字母的打印方式.请注意,如果打印了2个代码点,则只有一个可见的字形:

>>> print u'\u0d38'  # letter SA
?
>>> print u'\u0d3f'  # vowel sign I
 ?
>>> print u'\u0d38\u0d3f'  # both together
??
Run Code Online (Sandbox Code Playgroud)

马拉雅拉姆码点的宽度也不同; 如果你在SA下面添加ASCII字母和元音符号I,单独和组合,它看起来像这样:

>>> print u'\u0d38\nA..\n\u0d3f\nB..\n\u0d38\u0d3f\nAB.'  # with ASCII letters for size
?
A..
 ?
B..
??
AB.
Run Code Online (Sandbox Code Playgroud)

注意如何?宽度A(大约2.5倍宽),而??几乎与固定宽度的3个ASCII码点一样宽!然而,并非所有马拉雅拉姆语字母都如此宽泛.第一个例子中的下一个字母是U + 0D1F MALAYALAM LETTER TTA,它的宽度要小得多:

>>> print u'\u0d38\nA..\n\u0d1f\nB..'
?
A..
?
B..
Run Code Online (Sandbox Code Playgroud)

在实践中,我希望差异无关紧要,而且代码点组合在一起,使得输出结束大致相同的宽度.

接下来,马拉雅拉姆语也有其他组合字符; 你的第一个字符串有U + 0D4D MALAYALAM SIGN VIRAMA,它与前面的字母TTA结合在一起.

变音符号与前面的字母结合使用会对打印宽度造成严重破坏:

>>> print u'\u0d1f\nA..\n\u0d4d\nB..\n\u0d1f\u0d4d\nAB.'
?
A..
 ?
B..
??
AB.
Run Code Online (Sandbox Code Playgroud)

字母TTA与ASCII字母一样宽,当您添加virama标志时,宽度实际上没有变化.

您可以通过查看代码点Unicode常规类别来估算大小.该unicodedata.category()函数为您提供了一个字符串类别:

>>> import unicodedata
>>> unicodedata.category(u'\u0d38')
'Lo'
>>> unicodedata.category(u'\u0d3f')
'Mc'
>>> unicodedata.category(u'\u0d4d')
'Mn'
Run Code Online (Sandbox Code Playgroud)

字母SA是Lo(Letter,other),元音符号是Mc(Mark,间距组合),而virama符号是Mn(Mark,nonspacing).

>>> categories = {}
>>> for c in a[0]:
...     cat = unicodedata.category(c)
...     categories[cat] = categories.get(cat, 0) + 1
... 
>>> categories
{'Lo': 4, 'Mn': 1, 'Mc': 4, 'Zs': 1}
Run Code Online (Sandbox Code Playgroud)

因此对于第一个字符串,有4个字母,4个组合标记和一个元音符号.的Zs类别(隔膜,空间)是' 'ASCII空格字符.

如果我们跳过McMn字符,我们可以更好地预测它们的宽度吗?字符串a[0]宽度为5个字符(4次Lo和1个空格):

>>> print a[0] + '\nABCDE.'
?? ???????
ABCDE.
Run Code Online (Sandbox Code Playgroud)

在浏览器中,看起来不够接近,但在我的iTerm终端窗口中,它看起来像这样:

Python 2.7输出打印字符串<code>സിട്രീമിം</ code>和<code> ABCDE.</ code>,第二个字符串中的大写字母在屏幕上产生与第一行大致相同的宽度.

为了让你的线条排成一行,你必须为字符串计算正确的宽度,以便为显示宽度和代码点数量的差异添加额外的空格:

import unicodedata

def malayalam_width(s):
    return sum(1 for c in s if unicodedata.category(c)[0] != 'M')

form = u'{:<{width}}{:<3}({})'
for line in a:
    line = line[:12]
    adjust = len(line) - malayalam_width(line)
    print form.format(line, 1, 2, width=15 + adjust)
Run Code Online (Sandbox Code Playgroud)

这提高了输出很多已经:

端子输出,调整后的代码;  列排得更好,但仍然相隔太远

看起来那些更宽的字母确实有所作为.您必须手动为这些添加更多宽度才能获得更好的结果; 从字母到调整宽度的映射,你可以让它再次更好地对齐.但是,代码点宽度是由您使用的字体设置的,我不确定找到所有马拉雅拉姆字母使用相等宽度的字体是多么容易.

我发现使用制表位会更容易

form = u'{:<{width}}\t{:<3}({})'
for line in a:
    line = line[:12]
    adjust = len(line) - malayalam_width(line)
    print form.format(line, 1, 2, width=12 + adjust)
Run Code Online (Sandbox Code Playgroud)

现在数字排成一行:

排列有标签的列

你需要不断调整宽度; 否则你最终会在错误的制表符停止一半时间.

警告:我对马拉雅拉姆文字并不熟悉,我肯定错过了关于各种字母,元音符号和变音符号如何相互作用的细微之处.更熟悉脚本和Unicode代码点的人可能会产生比我在此处介绍的更好的宽度近似函数.

我也忽略了你最后一个字符串中当前存在的2 U + 200C ZERO WIDTH NON-JOINER代码点; 您可能希望从数据中删除这些内容.顾名思义,它也没有宽度.