几年前在使用openpyxl读写Excel的时候就发现,如果Excel内含切片器,那么openpyxl在保存Excel后,这些内容都会丢失;如果写入00:00时间,那么保存后时间会变成’29/12/1899 00:00:00’。因为是在本地端使用,所以可以通过换库,使用xlwings或pywin32库来解决这个问题。
这几天要用python处理Excel数据,发现最新版本的openpyxl(3.1.2)解决了0点时间的问题,看了下发布日志,是在2021年3月份发布3.0.7版解决的(https://openpyxl.readthedocs.io/en/stable/changes.html#),对应的此issue(https://foss.heptapod.net/openpyxl/openpyxl/-/issues/1510)。至于保存后丢失切片器的问题,依然没有解决。切片器的问题网上反映的也挺多的,库的作者也对此有过回应(https://groups.google.com/g/openpyxl-users/c/TKnnFMISB0c):
“Slicers are *not* part of the OOXML specification which is why they’re lost. There is currently no workaround for this. It should be possible to add support for such extensions to openpyxl at some point but it’s not a priority for me.”
因为这次是在服务端,xlwings或pywin32库无法在Linux上使用,所以就需要使用别的办法来解决——借用C#语言中dotnet core的库,毕竟日常也在用它来写东西。C#中读写Excel的库除了微软自家的COM外,最有名的便是Epplus了。
一开始想着是在python中直接调用C#代码,后来写着感觉麻烦,也不想把现有的相关openpyxl的代码再用C#写一遍,索性就写个C#独立的控制台应用,把openpyxl保存后的Excel再用Epplus读取生成切片器后保存,然后python再调用这个控制台应用做转换。经测试,完美达到我的目的。
顺便吐槽下openpyxl这个库,因为用惯了VBA和C#中Excel的那套对象模型,我对openpyxl操作Excel的写法总感觉不太习惯。
比如新建Excel并获取默认的Sheet,openpyxl要这样写:
wb = openpyxl.Workbook()
ws = wb[‘Sheet’]
明明新建Excel的默认Sheet名是“Sheet1”,在openpyxl却要写成“Sheet”,没有那个1,如果写成“Sheet1”,便会提示Sheet1不存在。
再比如,openpyxl好像只能使用列名(A, B, C…等)访问单元格区域,不能使用数字,像VBA或C#中“Range(Cells(1,1), Cells(5,8)”这样的写法在openpyxl中没有对应的写法。
再比如对于嵌套列表,只能采用for循环来逐行写入,没有VBA或C#的Resize方法。