Python计算字符宽度的方法
作者:zeo 发布时间:2021-02-13 20:25:28
本文实例讲述了Python计算字符宽度的方法。分享给大家供大家参考,具体如下:
最近在用python写一个CLI小程序,其中涉及到计算字符宽度,目标是以友好的方式将一个长字符串截取为等宽的片段。
对于unicode字符,python的len函数可以准确的计算其中所包含的字符个数,但是个数并不代表宽度,如:
>>>len(u'你好a')
3
因此无法简单的使用这种方式来计算宽度。
GBK decode
首先我想到GBK编码,00–7F范围内的字符是一字节编码,其余是双字节编码,正好与字符的宽度大体一致,于是有了这样的投机取巧的办法(假设取8个宽度):
>>> a = u'hello你好'
>>> b=a.encode('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
徘徊很久之后,偶然发现 Unicode Character Database 标准中有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的解决方案
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的官方EastAsianWidth 文档整理出字符宽度的范围表,然后使用unicode代码查表。使用之前的例子测试:
>>> get_width(ord(u'a'))
1
>>> get_width(ord(u'你'))
2
>>> get_width(ord(u'”'))
1
完全准确,而且在实际应用中的表现也比较好,是一个理想的解决方案,更多技巧请查阅urwid的old_str_util.py 源码。
希望本文所述对大家Python程序设计有所帮助。


猜你喜欢
- 用来批量切割视频的开头部分,比如去掉一部电视剧的序幕,看着难受不说数量还很多,很菜鸟的一篇,毕竟我也是一个菜鸟,首先要有ffmpeg这个软件
- 在我们使用log模块输出日志时,经常会遇到log输出重复的问题,如下:先来看这个文件log.py的代码:代码示例:''
- Python可以使用 xml.etree.ElementTree 模块从简单的XML文档中提取数据。 为了演示,假设你想解析Planet P
- 一、SQLite简介SQLite是一个包含在C库中的轻量级数据库。它并不需要独立的维护进程,并且允许使用非标准变体(nonstandard
- 本文例举了ASP实例关于ASP与ACCESS链接的两种方式。<%dim dd,connstrdd="data.mdb&quo
- 本文实例讲述了thinkphp3.x自定义Action、Model及View的实现方法。分享给大家供大家参考,具体如下:1、在xmall/L
- django-debug-toolbar介绍django-debug-toolbar 是一组可配置的面板,可显示有关当前请求/响应的各种调试
- 获取DataFrame虽然是一个比较简单的操作,但是有时候到手边就是写不出来,所以在这里总结记录一下:1.链表推倒式data =
- 1.pycharm运行python脚本的过程使用pycharm等编辑器run/debug运行python脚本时,编辑器会通过本地python
- 首先,创建一个存储过程 get_clob:t_name:要查询的表名;f_name:要查询的字段名;u_id:表的主键,查询条件;l_pos
- 前言最近在新的环境配置pycharm的项目时候,发现pycharm不能连接到mysql数据库。由于安了java环境但是还没配置相关的库,并且
- 本文实例讲述了Python类的继承、多态及获取对象信息操作。分享给大家供大家参考,具体如下:继承类的继承机制使得子类可以继承父类中定义的方法
- 概述Visdom:一个灵活的可视化工具,可用来对于 实时,富数据的 创建,组织和共享。支持Torch和Numpy还有pytorch。visd
- 女朋友是一个软件测试人员,在工作中经常会遇到需要录屏记录自己操作,方便后续开发同学定位。因为录屏软件动不动就开始收费,所以她经常更换录屏软件
- 敲了这么多年代码,每年都得画一些心啊花啊什么的,所以现在常规的已经有些倦怠了,至少也得来个三维图形才看着比较合理,而且光是三维的也没啥意思,
- 本文实例讲述了Python使用PyCrypto实现AES加密功能。分享给大家供大家参考,具体如下:#!/usr/bin/env python
- 详解微信小程序中的页面代码中的模板的封装 最近在进行微信小程序中的页面开发,其实在c++或者说是js中都
- 1.相关函数df.dropna()df.fillna()df.isnull()df.isna()2.相关概念空值:在pandas中的空值是&
- 一、代码注释介绍注释就是对代码的解释和说明,其目的是让人们能够更加轻松地了解代码。注释是编写程序时,写程序的人给一个语句、程序段、函数等的解
- 事务处理的流程示例 database := db.DB tx, err := datab