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)
您正在打印Malayalam Unicode代码点,它使用大量元音符号来修改前面的字形.这些元音符号代码点本身不会形成新的字母,并且Malayalam在终端中不会像ASCII字母那样产生相同的常规输出宽度.
例如,在您的第一个字符串中以U + 0D38 MALAYALAM LETTER SA和U + 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空格字符.
如果我们跳过Mc和Mn字符,我们可以更好地预测它们的宽度吗?字符串a[0]宽度为5个字符(4次Lo和1个空格):
>>> print a[0] + '\nABCDE.'
?? ???????
ABCDE.
Run Code Online (Sandbox Code Playgroud)
在浏览器中,看起来不够接近,但在我的iTerm终端窗口中,它看起来像这样:
为了让你的线条排成一行,你必须为字符串计算正确的宽度,以便为显示宽度和代码点数量的差异添加额外的空格:
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代码点; 您可能希望从数据中删除这些内容.顾名思义,它也没有宽度.