xlrd和openpyxl是pandas用于处理Excel文件的主要依赖库,前者用于读取.xls格式文件,后者用于读写.xlsx格式文件。在使用openpyxl配合pandas生成特定格式表格文件时,经常遇到这个奇怪问题:虽然设置表格某列为X个字符宽度,但打开生成的文件查看该列宽度,往往少于X。如下图所示,第一列实际列宽比脚本设置的列宽少了0.62个字符。

列宽缩水了!
这是怎么回事呢?如何确保脚本设置的列宽和在Excel中查看的列宽相同?这需要我们首先了解Excel如何设置列宽。
一、Excel列宽知识
Excel使用字符数(VBA中用ColumnWidth读写)设置列宽,这个字符是该字体中0-9里长得最胖的那个,一般是0。如果某列宽度为10,则该列能够容纳默认字体字号的10个字符0。列宽字符数也是我们查询列宽时看到的值。
列宽像素是这样计算的:默认字体为宋体/Arial 10磅时,每个字符宽7像素,字符两侧有5像素的留白(padding),或者内边距。下面的公式更加简单明了:
列宽像素=列宽字符数*7+5
举例:如果某列宽度为10字符,则该列宽为10*7+5=75像素。
Excel还用磅(points)来表示列宽(VBA中用Width属性查看)。计算公式如下:
列宽磅数=列宽像素*3/4
默认字体字号不同,字符0的像素宽度不同,留白像素也不同。下表列出常见默认字体的情况:
|
默认字体 |
默认字号(磅) |
字符宽度(像素) |
留白(像素) |
|
宋体 |
10 |
7 |
5 |
|
宋体 |
11 |
8 |
5 |
|
宋体 |
12 |
8 |
5 |
|
宋体 |
14 |
10 |
7 |
|
宋体 |
16 |
11 |
7 |
|
宋体 |
18 |
12 |
7 |
|
宋体 |
20 |
14 |
9 |
|
Arial |
10 |
7 |
5 |
|
Arial |
11 |
8 |
5 |
|
Arial |
12 |
9 |
7 |
|
Arial |
14 |
11 |
7 |
|
Arial |
16 |
12 |
7 |
|
Arial |
18 |
13 |
9 |
|
Arial |
20 |
15 |
9 |
|
Calibri |
10 |
7 |
5 |
|
Calibri |
11 |
7 |
5 |
|
Calibri |
12 |
8 |
5 |
|
Calibri |
14 |
10 |
7 |
|
Calibri |
16 |
11 |
7 |
|
Calibri |
18 |
12 |
7 |
|
Calibri |
20 |
14 |
9 |
|
微软雅黑 |
10 |
8 |
5 |
|
微软雅黑 |
11 |
9 |
7 |
二、openpyxl解决列宽“缩水”技巧
使用openpyxl生成.xlsx文件时,使用下列代码设置列宽:
sheet.column_dimensions['A'].width=X
这里的X表示字符数。但是在生成的.xlsx文件中,实际列宽总是“缩水”。目前没有深入研究openpyxl源代码,没有证据表明这是代码本身的缺陷。
另外,运行如下代码,可以知道openpyxl使用Calibri 11磅作为创建文件的默认字体和字号:
from openpyxl import Workbook, styles
wb=Workbook()
sheet=wb.active
default_font=styles.DEFAULT_FONT
print('default font:', default_font.name)
print('default size:', default_font.size)
sheet.save('test.xlsx')
但在Win10+Excel 2016环境下,test.xlsx的默认字体是宋体,而不是Calibri,原因未知。
如何解决这个小缺陷?可以考虑重新设置默认字体和字号,并在设置列宽时加上留白/字符宽度的比值。相当于加上了留白需要的字符数。示例代码如下:
from openpyxl import Workbook, styles
#重设默认字体和字号
styles.DEFAULT_FONT.name='宋体'
styles.DEFAULT_FONT.size=10
#新建工作簿
wb=Workbook()
sheet=wb.active
sheet.title='test'
#设置列宽为字符数+5/7(适用于默认字体为宋体/Arial 10磅)
sheet.column_dimensions['A'].width=10+5/7
wb.save('test.xlsx')
结果如下图所示:

三、总结
在实际工作中,我们经常使用pandas处理数据。虽然pandas处理数据内容的能力很强,但数据格式化输出的能力很弱,往往需要借助xlwt或openpyxl实现格式化输出。为了满足某些特定需求(比如表格套打),有时我们需要精确设置表格列宽。了解使用openpyxl设置列宽的坑以及避坑办法,可以帮助我们更好地解决精确设置表格列宽的问题。