电子表格格式的选择,看起来是个「导出时点下拉框」的小事,实际是数据在系统之间流动时会不会悄悄变形的问题。类型丢失、编码错乱、容量截断这三类故障有一个共同点:导出时不报错,导入时也不报错,只有在三个月后有人翻数据时才发现。
本站的 电子表格格式速查 给了各格式的静态属性对比,本文补上的是决策链条:面对一个具体的数据流,该选什么、为什么、以及出问题时怎么定位。
第一个决策点:谁来读这份数据
格式选择的第一个分岔不是「哪种格式更好」,而是这份数据的读者是谁。
给人看(运营要打开、销售要核对、客服要查)—— 用 XLSX。单元格有类型、可以带公式、有多张表,Excel 打开就能用。这是 CSV 无法提供的。
给程序读(下游 ETL、数据库导入、另一个服务消费)—— 优先 Parquet/Arrow,规模小时用 XLSX。
只做中间格式(系统 A 导出、系统 B 导入)—— CSV 仍然是最通用的交换格式,但必须显式约定编码与分隔符,并在文档里写明。
这个分岔决定了很多后续细节。给程序读的 CSV 可以放心扁平化,因为没人会手工打开它;给人读的 CSV 就要考虑 Excel 打开时的类型猜测问题。
类型丢失:CSV 的根本局限
CSV(Comma-Separated Values)这个名字就说明了它的定位——分隔符分隔的值。它没有类型系统、没有多表概念、没有元数据。每个字段就是一串字符。
这带来一串连锁反应。假设你的数据里有这些值:
| 原始数据 | CSV 里的实际内容 | Excel 打开后 |
|---|---|---|
订单号 00123 |
5 个字符 | 123,前导零消失 |
日期 2024-06-01 |
10 个字符 | 日期值,按本机格式重排 |
布尔 true |
4 个字符 | 布尔值 |
| 64 位 ID | 19 位数字 | 科学计数法,精度丢失 |
注意最后一行:这不是显示问题,是数据本身被改了。19 位的数字被截成 15 位有效数字,无法还原。
往返转换(导出再导入)会叠加这些损失。站内 Excel 转 CSV 与 Excel 转 JSON 的差别正在这里——JSON 有类型,CSV 没有。
决策规则很简单:只要需要保类型就不能用 CSV。 具体做法有三个:
- 改用 XLSX(最省心,但失去纯文本的可 grep 性)
- 把类型编进数据(日期写成 ISO 8601 带时区,ID 写成字符串并在表头声明)
- 用 JSON Lines(每行一个 JSON 对象,类型完整且仍可流式处理)
编码与 BOM:中文乱码的根因
编码问题的复杂度在于没有统一默认值。同一个 Excel,在不同系统上另存的编码不同:
- 中文 Windows:GBK
- 英文 Windows / macOS / Linux:UTF-8
再加上一个关键细节:不带 BOM 的 UTF-8 文件,Excel 双击打开时识别不可靠。于是有两种翻车方向:
- A 系统(Windows 中文)导出 GBK 无 BOM → B 系统(Linux)按 UTF-8 读 → 中文乱码
- A 系统导出 UTF-8 无 BOM → 对面用 Excel 打开 → 也乱码
它们的共同点是都没有声明。所以稳健方案是:
导出统一用 UTF-8 with BOM。 BOM 是一段固定的开头字节(EF BB BF),接收方靠它就能确定编码。Excel 与现代工具都能正确识别,唯一代价是某些老程序会把 BOM 当成一个多余字符(这时用 strip BOM 处理一次即可)。
读取端则要先探测再解码:
raw = open('data.csv', 'rb').read()
if raw.startswith(b'\xef\xbb\xbf'):
text, encoding = raw[3:].decode('utf-8'), 'utf-8-sig'
else:
text, encoding = raw.decode('gbk'), 'gbk' # 明确声明,而不是猜
最后那行注释是关键:不要靠猜。GBK 与 UTF-8 在短文本上视觉接近,猜错的概率不低。把编码写进交换文档,比任何自动检测都可靠。
除了编码,分隔符也有地区差异(逗号 / 分号 / 制表符),法国的 Excel 默认用分号。RFC 4180 规定 CSV 用逗号,遇到分号时整个文件会被当成单列。
容量:Excel 的硬限制与静默截断
Excel 的限制是硬性的:
- 单表 1,048,576 行 × 16,384 列
- 单元格最多 32,767 字符
超过之后的行为取决于工具,这是最危险的地方:pandas 会抛异常,但某些导出库会静默截断,只写出前 1,048,575 行而用户毫无察觉。文件能打开、格式正确、数据少了最后一行——这种故障可能几个月都发现不了。
超出限制时怎么选:
- 超过百万行 → Parquet(列式压缩,类型完整)
- 只是表格太宽 → 拆成多张表或转长格式(每行一个字段)
- 需要人读且数据量不大 → XLSX(可以分 sheet)
体积上,扫描件类的 PDF 有 95% 是图片,压缩 PDF 的本质也是压图片。同样的思路适用于表格:对占比最大的那一部分下手。一个 20MB 的 CSV 里如果日期列占了 8MB(因为格式冗长),把它改成 2024-06-01 就能省下大半。
什么时候该换格式
Parquet 的价值在规模上来之后才显现:
- 列式存储:分析查询通常只用 2–3 列,Parquet 只读这些列,速度差一个数量级
- 压缩率高:同质量数据约为 CSV 的十分之一
- 类型随文件保存:不存在 CSV 的类型丢失
经验阈值是单文件超过百万行或超过 1GB。在此之前,Parquet 增加的心智成本(人类无法直接 cat 看看里面是什么)与工具链复杂度大于收益。
中间还有 Arrow —— 内存中的列式格式,适合需要零拷贝转换的场景(Parquet → DataFrame 直接映射,不用解压)。
自查清单
- [ ] 明确这份数据的读者是人还是程序,据此选格式
- [ ] 需要保类型时不选 CSV(或把类型编进数据)
- [ ] 导出用 UTF-8 with BOM,读取先探测 BOM
- [ ] 交换文档里写明编码与分隔符,不靠猜
- [ ] 导出后核对行数是否与源数据一致(防静默截断)
- [ ] 数据量超过百万行时评估 Parquet
可复现的实测结果
用本站的 Excel 转 CSV 与 Excel 转 JSON 处理同一份含前导零与日期的表格,可以直接观察到类型差异:CSV 输出里 00123 就是 00123(若用 Excel 打开会变 123),JSON 输出里它保持字符串类型并在还原时仍带前导零。