Python计算字符宽度的⽅法
本⽂实例讲述了Python计算字符宽度的⽅法。分享给⼤家供⼤家参考,具体如下:
最近在⽤python写⼀个CLI⼩程序,其中涉及到计算字符宽度,⽬标是以友好的⽅式将⼀个长字符串截取为等宽的⽚段。
对于unicode字符,python的len函数可以准确的计算其中所包含的字符个数,但是个数并不代表宽度,如:
>>>len(u'你好a')
3
因此⽆法简单的使⽤这种⽅式来计算宽度。
GBK decode
⾸先我想到GBK编码,00–7F范围内的字符是⼀字节编码,其余是双字节编码,正好与字符的宽度⼤体⼀致,于是有了这样的投机取巧的办法(假设取8个宽度):
>>> a = u'hello你好'
>>> de('gbk')
>>> try:
...  print b[:8].decode('gbk')
... except:
...  print b[:7].decode('gbk')
...
hello你
如代码所⽰,⾸先将unicode的字符串进⾏GBK编码,然后截取8个字节的宽度后尝试⽤GBK解码,若解码失败,则少截取⼀个宽度,截取7个字节后使⽤GBK解码。
虽然初步解决了问题,但是这样做的硬伤很明显。⾸先代码不优雅,以试错的⽅式运⾏;其次GBK所能表⽰的字符有限,对于⼤量GBK编码以外的字符⽆法⽀持。
East_Asian_Width
徘徊很久之后,偶然发现标准中有East_Asian_Width 属性,并有以下可能值:
# East_Asian_Width (ea)
ea ; A    ; Ambiguous  不确定
ea ; F    ; Fullwidth  全宽
ea ; H    ; Halfwidth  半宽
ea ; N    ; Neutral  中性
ea ; Na    ; Narrow    窄
ea ; W    ; Wide    宽
其中除A不确定外,F/H/N/Na/W都能很明确的知道宽度,如果保守起见,将A视为宽度为2的话,则很容易给出单个字符的宽度:
>>> import unicodedata
>>> def chr_width(c):
...  if (unicodedata.east_asian_width(c) in ('F','W','A')):
...  return 2
...  else:
...  return 1
>>> chr_width(u'你')
2
>>> chr_width(u'a')
1
到现在似乎已经可以满⾜要求了,但是实际使⽤中发现属性为A的字符真不少见,最典型的就是中⽂的双引号:
>>> chr_width(u'”')
2
在⼤多数等宽字体中,中⽂双引号都是只占⼀位宽的,如果⼀⾏⾥有多个中⽂双引号,则累加的误判宽度将会使截取效果⼤打折扣,⽆疑这也不是最好的办法。
urwid的解决⽅案
是⼀个成熟的python终端UI库,它在curses的基础之上包装了类似HTML的控件⽤以显⽰⽂本内容,如果有这⽅⾯的开发需求,⾮常推荐此库,⽐直接使⽤curses库⽅便很多,⾮常棒的是它对unicode的⽂本宽度截取⾮常准确,让我⼤为惊讶,于是翻开它的源码⼀探究竟,⽂本宽度计算⽅⾯其核⼼代码如下:
widths = [
(126,  1), (159,  0), (687,  1), (710,  0), (711,  1),
(727,  0), (733,  1), (879,  0), (1154, 1), (1161, 0),
(4347,  1), (4447,  2), (7467,  1), (7521, 0), (8369, 1),
(8426,  0), (9000,  1), (9002,  2), (11021, 1), (12350, 2),
(12351, 1), (12438, 2), (12442,  0), (19893, 2), (19967, 1),
(55203, 2), (63743, 1), (64106,  2), (65039, 1), (65059, 0),
(65131, 2), (65279, 1), (65376,  2), (65500, 1), (65510, 2),
(120831, 1), (262141, 2), (1114109, 1),
]
def get_width( o ):
"""Return the screen column width for unicode ordinal o."""
global widths
if o == 0xe or o == 0xf:
return 0
for num, wid in widths:
if o <= num:
return wid
return 1
unicode所有字符如代码所⽰,⾸先根据⽂档整理出字符宽度的范围表,然后使⽤unicode代码查表。使⽤之前的例⼦测试:
>>> get_width(ord(u'a'))
1
>>> get_width(ord(u'你'))
2
>>> get_width(ord(u'”'))
1
完全准确,⽽且在实际应⽤中的表现也⽐较好,是⼀个理想的解决⽅案,更多技巧请查阅源码。
更多关于Python相关内容感兴趣的读者可查看本站专题:《》、《》、《》、《》、《》、《》及《》
希望本⽂所述对⼤家Python程序设计有所帮助。