Python中Unicode字符串的显示宽度

13

如何在Python 3.x中确定Unicode字符串的显示宽度,并且是否有一种方法可以使用该信息来将这些字符串与str.format()对齐?

动机示例: 在控制台打印字符串表格。 其中一些字符串包含非ASCII字符。

>>> for title in d.keys():
>>>     print("{:<20} | {}".format(title, d[title]))

    zootehni-           | zooteh.
    zootekni-           | zootek.
    zoothèque          | zooth.
    zooveterinar-       | zoovet.
    zoovetinstitut-     | zoovetinst.
    母                   | 母母

>>> s = 'è'
>>> len(s)
    2
>>> [ord(c) for c in s]
    [101, 768]
>>> unicodedata.name(s[1])
    'COMBINING GRAVE ACCENT'
>>> s2 = '母'
>>> len(s2)
    1

正如所见,str.format() 简单地将字符串中的代码点数(len(s))作为宽度,导致输出结果中的列不对齐。在 unicodedata 模块中搜索,我没有找到任何建议解决此问题的信息。

Unicode 标准化 可以解决 è 的问题,但无法解决亚洲字符的问题,因为它们通常具有更大的显示宽度。同样,存在零宽度的 Unicode 字符(例如,用于在单词内允许换行的零宽度空格)。您不能通过标准化来解决这些问题,因此请不要建议“标准化字符串”。

编辑:添加有关标准化的信息。

编辑2:在我的原始数据集中还有一些欧洲组合字符,即使经过标准化后也不会得出单个代码点:

    zwemwater     | zwemw.
    zwia̢z-       | zw.

>>> s3 = 'a\u0322'   # The 'a + combining retroflex hook below' from zwiaz
>>> len(unicodedata.normalize('NFC', s3))
    2

1
不,规范化并不能完全解决这个问题。它只能修复欧洲语言中的组合字符问题。然而,亚洲字符通常具有更大的显示宽度,再次破坏了str.format()函数,并引发了一个问题:“字符串的显示宽度是多少”。 - Christian Aichinger
请注意,如果您的控制台字体显示为窄字形或宽字形,那么东亚宽度字符实际上仍然取决于您的控制台字体。此时字符串格式化无能为力。 - Martijn Pieters
更新了答案。请告诉我如何使用“unicodedata.east_asian_width()”解决我的问题。据我所知,这是不可能的。例如,s2 = unicodedata.normalize('NFC', s)会返回所需的“LATIN SMALL LETTER E WITH GRAVE”。然后调用unicodedata.east_asian_width(s2)会返回"A",文档很好地告诉我们它是“模棱两可”的 - 尽管它的显示宽度肯定为1。 - Christian Aichinger
2
规范化对我的数据集没有解决问题。我刚刚发现了kitchen.text.display,它只适用于Python2,但似乎正好符合我的要求。 - Christian Aichinger
2
规范化和显示宽度是不同的主题。那些将此问题标记为重复的人完全错了。 - Walter Tross
显示剩余6条评论
1个回答

4
您有几个选项:
  1. 一些控制台支持转义序列,用于像素精确定位光标。但可能会导致一些重叠。

    历史注解:这种方法在Amiga终端中被用来通过打印一行文本然后将光标向下移动一个像素来显示控制台窗口中的图像。文本行的剩余像素逐渐构建了一个图像。

  2. 在代码中创建一个包含控制台/终端窗口中使用的字体中所有Unicode字符的实际(像素)宽度的表格。使用UI框架和一个小Python脚本生成此表格。

    然后添加计算使用此表格的文本的实际宽度的代码。结果可能不是控制台中字符宽度的倍数。与像素精确的光标移动一起,这可能解决您的问题。

    注意:您将需要为连字(fi,fl)和组合字符添加特殊处理。或者,您可以加载一个UI框架而不打开窗口,并使用图形基元来计算字符串宽度。

  3. 使用制表符(\t)缩进。但这只有在您的shell实际使用真实文本宽度来放置光标时才有用。许多终端将简单地计算字符。

  4. 创建一个HTML文件,其中包含一个表格,并在浏览器中查看它。


1
那个Amiga上的技巧是非常横向思维的。将可打印字符分成只有其最顶行像素的模式,就可以覆盖几乎所有你需要的内容。虽然叠加印刷听起来浪费,但如果没有其他图形选项,这个方法是聪明的。当你到达屏幕底部时会发生什么,我想你必须留下一行空白,或者你可以在部分屏幕外打印吗? - Davos
@Davos 你需要打印一行空格。超出底部的打印不起作用,因为终端窗口会开始滚动。而且这个技巧只适用于默认字体。 - Aaron Digulla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接