Python中显示Unicode字符串的宽度

13 投票
1 回答
4333 浏览
提问于 2025-04-17 21:07

我该如何在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 个回答

4

你有几个选择:

  1. 有些控制台支持特殊的转义序列,可以让光标精确到像素级别地定位。不过,这可能会导致一些文字重叠的问题。

    历史小知识:这种方法曾在Amiga终端中使用,通过在控制台窗口中打印一行文本,然后将光标向下移动一个像素来显示图像。剩下的文本像素慢慢拼凑成了一幅图像。

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

    然后添加代码,通过这个表格来计算文本的实际宽度。不过,结果可能并不是控制台字符宽度的整数倍。结合像素级的光标移动,这可能会解决你的问题。

    注意:你需要特别处理一些连字(比如fi、fl)和复合字符。另外,你也可以在不打开窗口的情况下加载一个UI框架,使用图形原语来计算字符串的宽度。

  3. 使用制表符字符(\t)来缩进。不过,这只有在你的终端实际上使用真实文本宽度来放置光标时才有用。很多终端只是简单地按字符计数。

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

撰写回答