我正在处理字符串格式。对于英语,格式是整洁的,但对于 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)
给出输出为
尽管
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)
给出:
最佳答案
您正在打印 Malayalam Unicode codepoints ,它使用了很多 vowel signs修改前面的字形。这些元音符号代码点本身不会形成新字母,马拉雅拉姆语不会在终端中产生与 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
സി
马拉雅拉姆语代码点的宽度也不同;如果在 SA 和元音符号 I 下方分别添加 ASCII 字母并组合,则如下所示:
>>> print u'\u0d38\nA..\n\u0d3f\nB..\n\u0d38\u0d3f\nAB.' # with ASCII letters for size
സ
A..
ി
B..
സി
AB.
注意如何
സ
比A
宽(大约是宽度的 2.5 倍),而 സി
几乎与固定宽度的 3 个 ASCII 代码点一样宽!然而,并非所有马拉雅拉姆语字母都这么宽。第一个例子中的下一个字母是 U+0D1F MALAYALAM LETTER TTA ,它的宽度要小得多:>>> print u'\u0d38\nA..\n\u0d1f\nB..'
സ
A..
ട
B..
在实践中,我希望差异无关紧要,而是将代码点组合在一起,以便输出最终具有大致相同的宽度。
其次,马拉雅拉姆语还有其他组合字符;你的第一个字符串有 U+0D4D MALAYALAM SIGN VIRAMA , 已与前面的字母 TTA 组合在一起。
变音符号与前面的字母组合时,会对打印宽度造成严重破坏:
>>> print u'\u0d1f\nA..\n\u0d4d\nB..\n\u0d1f\u0d4d\nAB.'
ട
A..
്
B..
ട്
AB.
字母 TTA 与 ASCII 字母一样宽,当您添加 virama 符号时,宽度实际上并没有改变。
您可以通过查看代码点 Unicode general categories 来估算大小.
unicodedata.category()
function为您提供字符串形式的类别:>>> import unicodedata
>>> unicodedata.category(u'\u0d38')
'Lo'
>>> unicodedata.category(u'\u0d3f')
'Mc'
>>> unicodedata.category(u'\u0d4d')
'Mn'
字母 SA 是
Lo
(字母,其他),元音符号为Mc
(标记,间距组合),virama 符号为Mn
(标记,非间距)。>>> 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}
所以对于第一个字符串,有 4 个字母、4 个组合标记和一个元音符号。
Zs
类别(分隔符,空格)用于 ' '
ASCII 空格字符。如果我们跳过
Mc
,我们能否更好地预测它们的宽度?和 Mn
人物?字符串 a[0]
将是 5 个字符宽(4 倍 Lo
和 1 个空格):>>> print a[0] + '\nABCDE.'
സി ട്രീമിം
ABCDE.
在浏览器中,这看起来不够接近,但在我的 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)
这已经大大提高了输出:
毕竟,那些更宽的字母似乎确实有所作为。您必须手动添加更多宽度以获得更好的结果;通过从字母到调整宽度的映射,您可以将其再次对齐。但是,代码点宽度是由您使用的字体设置的,我不确定找到一种对所有马拉雅拉姆语字母使用相同宽度的字体有多容易。
我发现使用制表位更容易,使用
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)
现在数字确实排列了:
您确实需要不断调整宽度;否则你有一半的时间会在错误的制表位上。
警告:我对马拉雅拉姆语脚本一点也不熟悉,而且我肯定会错过有关各种字母、元音符号和变音符号如何相互作用的微妙之处。更熟悉脚本和 Unicode 代码点的人可能会生成比我在此介绍的更好的宽度近似函数。
我也忽略了 2 U+200C ZERO WIDTH NON-JOINER当前存在于您的最后一个字符串中的代码点;您可能希望从您的数据中删除这些。顾名思义,它也没有宽度。
关于python - Unicode 格式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52931146/