指南 · 数据与表格

CSV 和 Excel 的那些坑,以及怎么避开

CSV 看起来是最简单的文件格式:一行一条记录,值之间用逗号隔开。可是从一个系统导出的 CSV,到了另一个软件里,经常出现中文乱码、编号前面的 0 不见了、好好的编码变成了日期、身份证号后三位全是 0。这篇指南逐个解释这些问题为什么会发生,以及怎样在不同系统之间搬运表格而不丢数据。

更新于 2026年9月28日

CSV 文件里到底有什么

CSV 是纯文本。每一行是一条记录,值之间用分隔符隔开,通常是逗号。如果某个值本身含有分隔符、双引号或换行,就用双引号把它括起来,值里面的双引号写成两个。这差不多就是 RFC 4180 的全部内容,也是 CSV 最接近「标准」的一份文件:

name,note,addr.city
"Smith, J.","said ""hi""",Paris

同样重要的是 CSV 里没有什么。它没有类型:CSV 没法表达「02134 是邮编,不是数字 2134」。它也不声明文字编码、不声明分隔符、没有任何格式。每个打开它的软件都得自己猜,下面这些问题,大多数就是猜错了。

坑一:中文乱码

双击打开一个 CSV,中文全变成了「浣犲ソ」或者一堆问号方块。文件里的字节并没有坏,只是软件用错误的编码去读了它。

现在的系统和网站导出 CSV,一般用 UTF-8 编码。而 Windows 上的 Excel 双击打开 CSV 时,长期以来默认按系统的旧代码页来读,中文系统上就是 GBK,除非文件开头带有一个声明「我是 UTF-8」的字节顺序标记(BOM)。反过来也一样:Excel 另存为时选「CSV(逗号分隔)」,在中文系统上写出的是 GBK 编码,拿到 Mac、Linux 或者网页上按 UTF-8 读,就是乱码。另存时选「CSV UTF-8(逗号分隔)」会自动带上 BOM,可以避免这个问题。

本站的 CSV ↔ JSON 转换,读文件时先按严格的 UTF-8 解码,失败后再按你选的旧编码解码(GBK/GB18030、Shift_JIS 或 Windows-1252)。中文 Excel 存出来的 GBK 文件,选 GBK 就能正确读出。导出给 Excel 用的 CSV 时,记得勾选「添加 BOM」。如果一个文件已经被反复打开、乱码状态下保存、再打开,损坏通常无法挽回,要回到最初的导出文件重新来。

坑二:所有内容挤在第一列

打开后每一行都挤在 A 列,逗号原样显示。这多半是分隔符对不上。在欧洲大部分国家,逗号是小数点,所以那里的 Excel 读写 CSV 都用分号:「Tea;1,50」表示两个值,第二个是 1.5。外企同事发来的这种分号文件,在中文 Excel 里双击打开就不会被拆开。

本站的转换工具会自动识别分隔符:在前几行里找一个字符(逗号、分号、制表符或竖线),它在每一行出现的次数都一样,引号里面的不算。像「name;price」「Tea;1,50」「Cake;3,20」这样的文件,虽然也有逗号,它仍会选分号。在 Excel 里,不要双击打开,而是用「数据 → 从文本/CSV」导入,导入对话框里可以明确指定分隔符和编码。

坑三:开头的 0 没了,身份证号后三位变成 000

邮编、工号、商品编码、以 0 开头的电话号码,打开后前面的 0 全没了,因为 Excel 会把看起来像数字的东西都当作数字。更严重的是,Excel 的数字只保留 15 位有效数字。18 位的身份证号 110101199003071234 会被存成 110101199003071000,并显示为 1.10101E+17。后三位就这样丢了,而且一旦保存,文件里写回的就是损坏后的值。银行卡号、订单号这类长编号同样中招。

本站把 CSV 转 JSON 时,如果开启了类型识别,以 0 开头的值,以及超过 JavaScript 安全整数范围的整数,都保留为文本,所以 02134 和 110101199003071234 都原样保留。在 Excel 里,用「数据 → 从文本/CSV」导入,在加载之前把这些列设为「文本」;或者在导出的系统里就把它们处理成文本。含有长编号的文件,千万不要用 Excel 打开后直接保存。

坑四:好好的值变成了日期

Excel 会把任何像日期的东西转换成日期。「1-2」会变成 1 月 2 日,「3/4」会变成 3 月 4 日,一些文字编码也逃不掉。最有名的受害者是人类基因的符号:SEPT1、MARCH1 过去经常被 Excel 变成「1-Sep」「1-Mar」,以至于人类基因命名委员会(HGNC)在 2020 年干脆把这些基因改了名,分别改成 SEPTIN1 和 MARCHF1。解决办法和保留开头的 0 一样:把这一列按文本导入。

以数字形式存储的日期

Excel 文件里真正的日期,存的是一个序列号,也就是从 1899 年底开始数的天数。序列号 45000 是 2023 年 3 月 15 日,45000.75 是 2023 年 3 月 15 日 18:00。转换时有两个怪癖要注意。第一,为了和 Lotus 1-2-3 兼容,Excel 故意把 1900 年当作闰年,所以 1900 年 3 月 1 日之前的序列号会差一天。第二,用旧版 Mac Excel「1904 日期系统」创建的工作簿,从 1904 年开始数:同样是 45000,表示的是 2027 年 3 月 16 日,晚了整整 1462 天。本站的 Excel 转 CSV 会读取每个工作簿用的是哪种日期系统,两种都能正确换算。

坑五:藏在数据里的公式

以 =、+、-、@ 开头的值,在电子表格里打开时可能被当成公式执行。如果你的系统导出的是用户填写的内容,比如姓名、备注、地址,就有人可以填进一个公式,等同事打开导出文件时它就会运行。OWASP 把这类问题称为 CSV 注入。常见的防御办法,是在导出给电子表格用的文件时,给这类值前面加一个单引号或制表符;如果数据是给其他程序用的,就保持原样,把风险控制在电子表格这一端。

从 Excel 里干净地取出数据

要从工作簿里取出数据,最可靠的办法是根本不让 Excel 来写 CSV。本站的 Excel 转 CSV 直接在浏览器里读取 .xlsx 文件,从而绕开了代码页和数字显示格式的问题:它取每个单元格实际存储的值,日期写成 ISO 格式(2023-03-15),长数字按存储的值原样写出,编码统一用 UTF-8。选择工作表,看一眼预览,下载 CSV 或 JSON 即可。

它只读新版 .xlsx 格式。Excel 97–2003 的老式 .xls 文件是另一种二进制格式,需要先用 Excel 或 WPS 另存为 .xlsx。公式导出的是它最后一次计算的结果,这也是 CSV 唯一能保存的东西。

把表格以文字形式分享

写 README、提 issue、在聊天里发一张小表,Markdown 表格往往比附件更方便。把从 Excel 或在线表格里复制的单元格粘贴到 Markdown 表格工具里(从电子表格复制出来的是制表符分隔的文本,工具能直接识别),为每一列选好对齐方式,复制结果即可:

| 城市 |  Pop |
| :--- | ---: |
| 北京 | 21.9 |
| Oslo |  0.7 |

即使有中文,列在等宽字体的编辑器里也能对齐,因为每个汉字按两个字符宽计算。数字列最好右对齐,这样小数点能对齐。

搬运数据的检查清单

  1. 导出时用 UTF-8。如果对方会用 Excel 打开,加上 BOM。
  2. 弄清楚分隔符,用明确指定分隔符的方式导入,而不是双击打开。
  3. 标识类的列,包括身份证号、工号、邮编、电话、各种编码,在任何电子表格碰到它们之前就设为文本。
  4. 导出日期用 ISO 格式(YYYY-MM-DD),在任何地区设置下都不会有歧义。
  5. 前后对比行数。某个值里有换行却没有正确加引号,就会多出几行。
  6. 原始导出文件保持不动,在副本上操作。
  7. 交给别人之前,自己先用对方会用的软件打开一次,看看有没有乱码、丢 0、变成日期的情况。

局限

  • 自动识别只是猜测。 分隔符和编码识别对常见文件有效;只有一列的文件或者样本太短时可能判断错,所以工具允许手动指定两者。
  • 电子表格的行为因版本和地区而异。 本文描述的 Excel 行为由来已久,但新版本增加了关闭部分自动转换的选项,请查看你所用版本的导入设置。WPS 的行为与 Excel 大体相同,细节可能不同。
  • 只能读取 .xlsx。 老式 .xls 和 .ods 需要先转换。
  • 一切都在浏览器里运行, 所以特别大的文件受限于你设备的内存。

常见问题

CSV 用 Excel 打开中文乱码怎么办?

Excel 用 GBK 去读了 UTF-8 文件。导出时选择带 BOM 的 UTF-8,或者用「数据 → 从文本/CSV」导入并选择 UTF-8。反过来,Excel 存出的 GBK 文件在本站转换时选择 GBK 编码即可。

怎么保留编号开头的 0?

0 其实还在文件里,是 Excel 打开时去掉的。把这一列按文本导入,或者用一个按原样保留值的工具转换。

身份证号为什么后三位变成了 000?

Excel 只保留 15 位有效数字。把这类列按文本导入,并且不要用 Excel 重新保存含有长编号的文件。

参考资料

本文用到的工具

↵ 打开 · Esc 关闭