1.问题场景
xlwt是一个生成xls文件的python代码包,一般配合pandas使用。 xlwt中设置表格列宽的代码如下:
sheet.col(0).width=X #X是一个小于65536的正整数,否则系统报错。
但是这个 X 代表什么? 与我们用Excel打开文件后看到的列宽有什么对应或映射关系? 如果需要精确控制列宽,如何设置这个值?
xlwt设置的宽度和Excel中显示的列宽有什么关系?
有一篇文章说在xlwt中,列宽的度量单位是一个字符的1/256。 也就是说,如果你需要设置10个字符的列宽,这个值就是2560。但是如果你编写并运行上图左边的代码生成一个xls文件,那么第一列的宽度就是设置为2560,打开这个文件查看,会发现第一列的宽度是9.29,而不是10,这个说法显然不成立。
要回答这些问题,我们首先需要了解我们在 Excel 中看到的列宽是什么。
2. Excel中的列宽表示
我们在Excel中看到的列宽就是该列可以容纳的字符数。 注意:是字符数! 而这个字符并不是任意的,而是Excel文件默认字体中的字符0。
示例:如果默认字体为 Arial 10 磅,列宽为 8.43(默认列宽),则该列可以容纳 8.43 Arial 10 磅 0。
我们可以设置列宽为10,然后输入10个0进行验证。
Excel用字符数来表示列宽,符合人的直觉,使用起来更方便。
在Excel VBA中,通过Columns(1).ColumnWidth读取和写入列宽字符excel列宽单位,通过Columns(1).Width读取列宽点数(points),但不能写入。
Sub set_width()
For i = 2 To 21
Columns(i).ColumnWidth = i - 1 '第2-21列设置列宽字符数为1-20, 第1列保持默认
Next
For i = 1 To 21
Cells(1, i) = Columns(i).ColumnWidth '显示第1-21列的列宽字符数
Cells(2, i) = Columns(i).Width * 4 / 3 '列宽像素
Cells(3, i) = Columns(i).Width '列宽磅值
Next
End Sub
列宽字符、列宽点数、列宽像素的换算公式如下(默认字体为Arial/宋体10点):
公式一:列宽字符数*7+5=列宽像素数
公式二:列宽点数=列宽像素数*3/4(当DPI为96时)
公式 3:以像素为单位的列宽 = 以磅为单位的列宽 * 4/3
公式1涉及的知识比较复杂。 可以下载ISO/IEC 29500-1:2016标准文件,阅读1601页关于宽度的解释,仔细理解下面这段话:
列宽测量为数字 0、1、2、…、9 的最大数字宽度的字符数,以正常样式的字体呈现。 有 4 个像素的边距填充(每边两个),加上 1 个用于网格线的像素填充。
三、xlwt中的宽度与Excel列宽字符数的对应关系
xlwt如何存储width的值,Excel如何将该值转换为列宽的字符数,本文不做深入讨论。 在xlwt包中的Column.py文件中,开发者定义了宽度转换width_in_pixels的公式:
def width_in_pixels(self):
# *** Approximation ****
return int(round(self.width * 0.0272 + 0.446, 0))
根据公式,xlwt的width_in_pixels只是根据width计算出来的一个近似值,两者没有线性关系。 简单测试发现近似像素和实际像素有0-2的误差。
虽然在大多数情况下0-2个像素的误差无所谓,但如果需要将宽度精确控制到像素级别(比如打印),这个公式显然不够准确。 我们需要一个更精确的计算公式。
4.宽度精确计算公式
经过多次测试,终于找到了这个公式:
公式四:width=round(列宽字符*256+182.8571,0)
解释:在xlwt中,一个字符的宽度是256,相当于7个像素,那么字符两边5个像素=256*5/7=182.8571。 由于宽度只能是整数,所以使用round函数进行四舍五入(虽然python的round函数有错误,但没有出现在这个公式中)。
这个公式帮助我们理解了xlwt列宽与Excel列宽字符数的对应关系excel列宽单位,并且设置列宽有了新的思路:只考虑列宽字符数(再次强调:这是符合人的直觉),然后用 公式计算宽度到底是多少。
根据以上4个公式,很容易写出一个基于像素和点值的列宽计算公式:
公式5:width=round((pixel-5)/7*256+182.8571,0)
公式6:width=round((磅数*4/3-5)/7*256+182.8571,0)
注意:公式5中的像素点只能是正整数,公式6中的点数可以是小数,但小数点后两位只能是以下三个数字之一:25、50、75。
另外,Excel的列宽字符数可以设置为整数或小数,但小数点后两位只支持14、29、43、57、71、86(即使强制更改为其他小数,Excel 也会自动将其更改回 )。 通过这些值,可以将列宽精度控制到像素级,比如4个字符=33个像素,4.14个字符=34个像素,4.29个字符=35个像素,4.43个字符=36个像素,等等。 不想动脑的朋友可以使用下面的python脚本生成字符、像素和xlwt_width的对照表:
print('字符数t像素txlwt_width')
for pixel in range(12, 272):
chars=round((pixel-5)/7,2)
width=round(chars*256+182.8571)
print(f'{chars}t{pixel}t{width}')
注意,以上公式和解释仅适用于Excel默认字体为Arial/宋体10pt的情况。 由于xlwt默认使用的字体和字号都是Arial 10磅,所以编程时不需要考虑不同的默认字体。 需要修改默认字体的朋友可以在xlwt包Formatting.py中找到Arial进行修改,然后保存。 但是不建议修改源码。
五、后记
虽然xlwt从2018年开始就停止更新了,不支持现在流行的xlsx文件格式,但是功能比较齐全的xlwings也很好用,但是这个包提供的方法少而精,参数简单易懂,非常好用,并且可以很好的配合pandas输出数据格式的报表。 在实际工作中,很多管理系统的交换文件仍然使用xls格式,所以这个包会继续受到程序员的喜爱。 希望文中提出的列宽计算公式可以解决在使用xlwt过程中可能遇到的小麻烦。