造一个最小的例子就能看清:表头合并A1:D1,中间再来一处B5:C5合并。
xlsx读回来是这样:
('2026 年 11 月流水', None, None, None)
('日期', '项目', '金额', '备注')
('2026-11-01', '茶饼', 1280, None)
(None, None, None, '小计在上')
(None, '合并说明', None, None)
('2026-11-02', '散茶', 960, None)同样的内容存成csv再读回来:
row0: '2026 年 11 月流水,,,' row1: '日期,项目,金额,备注' row2: '2026-11-01,茶饼,1280,' row3: ',,,小计在上' row4: ',合并说明,,' row5: '2026-11-02,散茶,960,'
差别在哪
| xlsx | csv | |
|---|---|---|
| 合并信息 | 可查merged_cells.ranges,能看出「这几个格是同一格」 | 完全丢失,变成若干空字段 |
| 非左上角单元格 | 读回None(值只在左上角那一格) | 空字符串,与「真的没填」不可区分 |
| 空字符串与空单元格 | 都读回None | 都是连续逗号 |
| 能不能还原表头层级 | 能,靠合并区域推断 | 不能,只能看到一行文字加三个空 |
最要命的是第二行:csv里「合并说明」看起来就在B列,A列和C列是空的。你没法判断它是合并出来的居中标题,还是B列真的有个值而旁边没填。xlsx里查一下合并区域就知道了。
实用取舍
- 要保留结构(表头层级、分组小计):给xlsx,并在指令里要求它先报合并区域。
- 只要数据行(干净的二维表、一列一个字段):csv更省token,也不会有合并带来的歧义。
- 不确定:先给xlsx让它描述结构,确认后再转成csv处理数据。两步比一步稳。
还有一个反直觉的点:写进单元格的空字符串,读回来是None,不是空字符串。所以「这一格是空的」和「这一格写了空」在读取层面分不出——需要区分就在表里加一列显式标记。