如何控制包含东亚字符的Unicode字符串的内边距

9 投票
7 回答
5699 浏览
提问于 2025-04-16 09:32

我有三个UTF-8字符串:

hello, world
hello, 世界
hello, 世rld

我只想要前10个ASCII字符的宽度,这样括号就能在同一列显示:

[hello, wor]
[hello, 世 ]
[hello, 世r]

在控制台中:

width('世界')==width('worl')
width('世 ')==width('wor')  #a white space behind '世'

一个中文字符占用三个字节,但在控制台显示时只占用两个ASCII字符的宽度:

>>> bytes("hello, 世界", encoding='utf-8')
b'hello, \xe4\xb8\x96\xe7\x95\x8c'

Python的format()在混合UTF-8字符时并没有帮助

>>> for s in ['[{0:<{1}.{1}}]'.format(s, 10) for s in ['hello, world', 'hello, 世界', 'hello, 世rld']]:
...    print(s)
...
[hello, wor]
[hello, 世界 ]
[hello, 世rl]

效果不好看:

 -----------Songs-----------
|    1: 蝴蝶                  |
|    2: 心之城                 |
|    3: 支持你的爱人              |
|    4: 根生的种子               |
|    5: 鸽子歌(CUCURRUCUCU PALO|
|    6: 林地之间                |
|    7: 蓝光                  |
|    8: 在你眼里                |
|    9: 肖邦离别曲               |
|   10: 西行( 魔戒王者再临主题曲)(INTO |
| X 11: 深陷爱河                |
| X 12: 钟爱大地(THE MO RUN AIR |
| X 13: 时光流逝                |
| X 14: 卡农                  |
| X 15: 舒伯特小夜曲(SERENADE)    |
| X 16: 甜蜜的摇篮曲(Sweet Lullaby|
 ---------------------------

所以,我想知道有没有标准的方法来处理UTF-8的填充问题?

7 个回答

5

看看这个kitchen吧。我觉得它可能有你需要的东西在这里

6

看起来这个问题没有官方的支持,但有一个内置的包可能会有所帮助:

>>> import unicodedata
>>> print unicodedata.east_asian_width(u'中')

返回的值表示这个字符的 类别。具体来说,

  • W - 东亚宽字符
  • F - 东亚全角字符(窄字符的全角)
  • Na - 东亚窄字符
  • H - 东亚半角字符(宽字符的半角)
  • A - 东亚模糊字符
  • N - 不是东亚字符

这个回答给出了一个快速的解决方案。不过要注意,显示的结果取决于你使用的具体等宽字体。ipython 和 pydev 默认的字体效果不好,而 Windows 控制台的效果还可以。

16

当你想把ASCII文本和中文用等宽字体对齐时,有一组ASCII字符的全角版本可以使用。下面我做了一个ASCII字符到全角版本的对照表:

# coding: utf8

# full width versions (SPACE is non-contiguous with ! through ~)
SPACE = '\N{IDEOGRAPHIC SPACE}'
EXCLA = '\N{FULLWIDTH EXCLAMATION MARK}'
TILDE = '\N{FULLWIDTH TILDE}'

# strings of ASCII and full-width characters (same order)
west = ''.join(chr(i) for i in range(ord(' '),ord('~')))
east = SPACE + ''.join(chr(i) for i in range(ord(EXCLA),ord(TILDE)))

# build the translation table
full = str.maketrans(west,east)

data = '''\
蝴蝶(A song)
心之城(Another song)
支持你的爱人(Yet another song)
根生的种子
鸽子歌(Cucurrucucu palo whatever)
林地之间
蓝光
在你眼里
肖邦离别曲
西行(魔戒王者再临主题曲)(Into something)
深陷爱河
钟爱大地
时光流逝
卡农
舒伯特小夜曲(SERENADE)
甜蜜的摇篮曲(Sweet Lullaby)
'''

# Replace the ASCII characters with full width, and create a song list.
data = data.translate(full).rstrip().split('\n')

# translate each printable line.
print(' ----------Songs-----------'.translate(full))
for i,song in enumerate(data):
    line = '|{:4}: {:20.20}|'.format(i+1,song)
    print(line.translate(full))
print(' --------------------------'.translate(full))

输出

 ----------Songs-----------
|   1: 蝴蝶(A song)          |
|   2: 心之城(Another song)   |
|   3: 支持你的爱人(Yet another s|
|   4: 根生的种子               |
|   5: 鸽子歌(Cucurrucucu palo|
|   6: 林地之间                |
|   7: 蓝光                  |
|   8: 在你眼里                |
|   9: 肖邦离别曲               |
|  10: 西行(魔戒王者再临主题曲)(Into s|
|  11: 深陷爱河                |
|  12: 钟爱大地                |
|  13: 时光流逝                |
|  14: 卡农                  |
|  15: 舒伯特小夜曲(SERENADE)    |
|  16: 甜蜜的摇篮曲(Sweet Lullaby|
 --------------------------

虽然看起来不是特别好,但它们是对齐的。

撰写回答