所以我想对齐包含非 ascii 字符的字段。以下似乎不起作用:
for word1, word2 in [['hello', 'world'], ['こんにちは', '世界']]:
print "{:<20} {:<20}".format(word1, word2)
hello world
こんにちは 世界
有解决办法吗?
最佳答案
您正在格式化一个多字节编码的字符串。您似乎正在使用 UTF-8 对文本进行编码,并且该编码每个代码点使用多个字节(在 1 到 4 之间,具体取决于特定字符)。格式化字符串计算字节,而不是代码点,这是您的字符串最终未对齐的原因之一:
>>> len('hello')
5
>>> len('こんにちは')
15
>>> len(u'こんにちは')
5
改为将您的文本格式化为 Unicode 字符串,这样您就可以计算代码点,而不是字节数:
for word1, word2 in [[u'hello', u'world'], [u'こんにちは', u'世界']]:
print u"{:<20} {:<20}".format(word1, word2)
您的下一个问题是这些字符也比大多数宽;你有双宽代码点:
>>> import unicodedata
>>> unicodedata.east_asian_width(u'h')
'Na'
>>> unicodedata.east_asian_width(u'世')
'W'
>>> for word1, word2 in [[u'hello', u'world'], [u'こんにちは', u'世界']]:
... print u"{:<20} {:<20}".format(word1, word2)
...
hello world
こんにちは 世界
str.format()
无法处理该问题;您必须在格式化之前根据 Unicode 标准中注册为更宽的字符数手动调整列宽。
这很棘手,因为有不止一种宽度可用。查看East Asian Width Unicode standard annex ;有窄、宽和模糊宽度;窄是大多数其他字符打印的宽度,宽是我终端上的两倍。模棱两可的是......关于它实际显示的宽度是模棱两可的:
Ambiguous characters require additional information not contained in the character code to further resolve their width.
它们的显示方式取决于上下文;例如,希腊字符在西方文本中显示为窄字符,但在东亚文本中显示为宽字符。我的终端将它们显示为窄,但其他终端(例如,针对东亚语言环境配置)可能将它们显示为宽。我不确定是否有任何万无一失的方法来弄清楚它是如何工作的。
在大多数情况下,您需要为 unicodedata.east_asian_width()
计算具有 'W'
或 'F'
值的字符担任 2 个职位;从您的格式宽度中为每一个减去 1:
def calc_width(target, text):
return target - sum(unicodedata.east_asian_width(c) in 'WF' for c in text)
for word1, word2 in [[u'hello', u'world'], [u'こんにちは', u'世界']]:
print u"{0:<{1}} {2:<{3}}".format(word1, calc_width(20, word1), word2, calc_width(20, word2))
然后这会在我的终端中生成所需的对齐方式:
>>> for word1, word2 in [[u'hello', u'world'], [u'こんにちは', u'世界']]:
... print u"{0:<{1}} {2:<{3}}".format(word1, calc_width(20, word1), word2, calc_width(20, word2))
...
hello world
こんにちは 世界
您可能在上面看到的轻微错位是您的浏览器或字体对宽代码点使用了不同的宽度比(不是两倍)。
所有这一切都需要注意:并非所有终端都支持东亚宽度 Unicode 属性,并且仅以一个宽度显示所有代码点。
关于python - 格式化包含非 ascii 字符的列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34655347/