文章
为什么 Excel 会搞乱你的 CSV 文件
一个以三个零开头的患者编号。一个叫 SEPT2 的基因。一个十六位的账号。把这些内容中的任何一个,以普通 CSV 文件的形式在电子表格里打开,Excel 的导入功能不只是显示它们,而是改写它们:零消失了,基因名变成了日期,账号丢掉了最后几位数字。这些都算不上真正的错误,而是一个通用的数字和日期猜测器,遇到只是长得像数字或日期的文本时会发生的事情。下面是 Excel 一打开 CSV 文件时实际发生的事情,以及如何在这一切发生之前阻止它。
前导零会消失
在单元格中输入 02134,Excel 默认的“常规”列类型会把它当作数字读取,丢掉前导零:变成 2134。这会影响任何以数字形式存储、但本质上并非数量的编码,比如美国邮政编码、法国邮政编码、员工或发票编号、电话号码。从底层 CSV 文件的角度看,技术上什么都没丢失,单元格旁边的文本一直保持不变,直到你保存工作簿,这时 Excel 才会把它显示出来的内容写回去,零就永久消失了。解决办法是在 Excel 有机会猜测之前,就告诉它这一列是文本:通过“数据 > 自文本/CSV”(或“获取数据 > 自文件”),你可以显式设置每一列的数据类型,或者在单个单元格的值前面加一个撇号,强制将其视为文本。

文本会未经提示地变成日期
同一个猜测器也会寻找形似日期的文本。一个包含 MAR1 或 SEPT2 的单元格,会被当作 3 月 1 日或 9 月 2 日读取,在没有任何警告的情况下悄悄转换成日期值并重新格式化。这并非假设:2020 年,人类基因命名委员会(HUGO)重新命名了大约 27 个人类基因符号,包括 SEPT2 和 MARCH1,原因正是电子表格软件每次打开数据集时都会把它们变成日期,这个问题严重到一项 2016 年针对基因组学论文的调查发现,在附带 Excel 补充文件的出版物中,大约五分之一都存在这类自动更正错误。这个教训不仅限于基因名:任何恰好看起来像“日+月”的简短字母数字编码,只要有人在电子表格里打开你的 CSV 文件,就有风险,不只是你自己打开时才会。
很长的数字会丢失末尾几位
Excel 把每个数字都存储为精度为 15 位有效数字的浮点值,这是微软官方文档直接注明的限制。像 4111111111111111 这样的十六位银行卡或账号会被四舍五入以适应这个精度,导致最后一两位数字悄悄变成零,数值不再是你输入的那个。之后再对单元格进行格式设置,也无法把缺失的数字找回来,因为底层存储的数字早已被四舍五入;唯一的解决办法,是在 Excel 把它当作数字处理之前,就把这一列导入为文本,或者在数值前加上撇号。

带重音符号的文字会变成乱码
编码问题是和自动更正完全不同的另一种故障模式。一个没有字节顺序标记的 CSV 文件本质上只是一串字节;如果它是以 UTF-8 保存的,Windows 上的 Excel 通常会假定使用系统自身的代码页来打开它,导致带重音符号的字母、货币符号和表情符号变成乱码,比如 café 这样的字符串会显示成类似 café 的样子。为 Excel 生成 CSV 文件时,稳妥的做法是保存为“CSV UTF-8”,或添加 UTF-8 字节顺序标记;读取 CSV 文件时,稳妥的做法是通过“数据 > 自文本/CSV”导入,这样可以显式选择源文件编码,而不是依赖双击打开时的自动猜测。
在格式之间移动数据而不用 Excel 打开它
严格来说,这些都不是 Excel 的错误,而是电子表格的数字和日期猜测器本该做的事,无论文件用什么方式打开都会发生,不只是在 Excel 里。要重塑 CSV 文件,或者在它进入任何电子表格之前先确认某一列确实保持文本状态,最稳妥的办法是直接处理它:我们的数据转换工具使用标准的、能正确处理引号的解析器来解析 CSV,从不重新解释数值的类型,因此邮政编码或基因名会原样保留为你给它的文本。它完全在你的浏览器中运行,你粘贴的任何内容都不会被上传到任何地方。不过这只能保护转换这一步:如果输出结果之后仍要在电子表格中打开,也需要在那里把敏感列标记为文本。
本文涉及的工具
常见问题
如何阻止 Excel 从 CSV 中删除前导零?
不要通过双击打开文件,而是通过“数据 > 自文本/CSV”(或“获取数据”)导入,它会显示逐列的数据类型选择器;在完成导入之前,把邮政编码、编号或电话号码所在的列设为“文本”。针对单个单元格的快速修复方法,是在数值前输入一个撇号,比如 '02134,这样可以强制其为文本,且不改变显示效果。
为什么 Excel 把我的编号或代码变成了日期?
Excel 的“常规”列类型会主动寻找形似日期的文本,比如“一个短单词加一个数字”,或者“用短横线或斜杠隔开的两个数字”这样的模式,并在没有任何确认提示的情况下转换任何匹配的内容。这正是 2020 年 SEPT2 和 MARCH1 等基因符号被迫改名的原因。和处理前导零一样,把该列导入为“文本”,就能在这种猜测发生之前阻止它。
用别的方式转换 CSV,能完全避免这些问题吗?
只能在那一次转换步骤中避免这些问题,而不是永久解决。像我们的数据转换工具这样把每个字段都当作纯文本处理的解析器,在把 CSV 转换成 JSON 时不会改写邮政编码或基因名。但如果输出结果之后仍会被打开在电子表格中,不管是你自己还是接收者打开,那个程序自己的导入功能又会有一次重新解释这些数值的机会,所以敏感列在那里也需要再标记为文本。