Windows 上写 CSV 为什么每行多一个空行?文本模式换行翻译的两个坑

举报
yd_232353574 发表于 2026/11/03 08:29:37 2026/11/03
【摘要】 实测 Windows 文本模式会把换行翻成 CRLF、CSV 缺 newline 会写出双 CRLF 导致每行多一个空行;并讲清 UTF-8 BOM 让第一列多不可见字符的读侧解法,以及 GBK 报错字节特征的识别。

一个查了两小时的问题

读一份别人给的 CSV,用 csv.DictReader 读出来,每行后面都多一个空行,有些行还被拆成了两行。排查半天才发现,是写入时换行符被翻译了两次。

这其实是 Windows 上写文件最常踩、又最不容易一眼看出的两个坑:文本模式的换行翻译,以及 UTF-8 的 BOM。下面把每一处都打到字节层面看清楚。

Windows 文本模式的换行翻译

Windows 的行尾是 CRLF(\r\n)。Python 在文本模式写入时会做换行翻译:你代码里写 \n,落盘时变成 \r\n;读的时候又翻回 \n。这个翻译只在文本模式发生,二进制模式不碰。

我建了四个文件,写入同一份内容 'a\nb\nc\n',只看落盘的字节:

  • 默认文本模式写 → 字节是 a\r\nb\r\nc\r\n
  • newline='' 写 → 字节是 a\nb\nc\n
  • 二进制写 → 字节是 a\nb\nc\n
  • newline='\r\n' 写 → 字节是 a\r\nb\r\nc\r\n

第一行结论:文本模式下你写的 \n 会变成 \r\n。很多人以为「我用 \n 换行就一定是 LF」,在 Windows 上并不成立。

CSV 的 \r\r\n 双翻译陷阱

csv.writer 自己就会在每行行尾写 \r\n(这是 CSV 规范规定的,不是 Python 自作主张)。问题出在:如果你用默认文本模式打开文件,Python 先把你写的 \n 翻成 \r\n,csv 模块又在这一层外面再写一层 \r\n,叠出来就是 \r\r\n。

读的时候,多出来的那个 \r 被当成一行的结尾,于是每行后面都多一个空行,数据行被凭空拉长,按行号取数全错位。

所以写 CSV 一定要 newline='',让 csv 模块自己管换行、Python 不再二次翻译:

import csv
with open('out.csv', 'w', newline='', encoding='utf-8') as f:
    w = csv.writer(f)
    w.writerow(['name', 'id'])

BOM 让第一列多出一个字符

另一个常见问题是:第一列的列名前面多了个看不见的字符,导致 row['name'] 一直报 KeyError。

用 Excel「另存为 UTF-8 CSV」会在文件头写 BOM(字节 ef bb bf)。如果你用 utf-8 编码读,BOM 会留在第一列名前面,变成 '\ufeffname';用 utf-8-sig 读,BOM 在解码时自动剥掉,得到干净的 'name'。

实测:建一个用 utf-8-sig 写的文件,内容是 name,id,字节是:

ef bb bf 6e 61 6d 65 2c 69 64

然后两种编码读回来:

  • 用 utf-8 读,第一行是 '\ufeffname,id'
  • 用 utf-8-sig 读,第一行是 'name,id'

utf-8-sig 这个编码名已经说明用途:读的时候跳过 BOM,写的时候自动加上 BOM。

网上常见的另一种说法是「导出时主动加 BOM 防乱码」,那是专门给 Excel 双击打开用的。一旦文件要交给 Python 去读,BOM 反而会让第一列列名带 \ufeff,所以读取侧统一用 utf-8-sig 更稳——有就剥掉,没有就当普通 UTF-8。

处理 CSV 时我一般直接上 utf-8-sig:

import csv
with open('data.csv', newline='', encoding='utf-8-sig') as f:
    reader = csv.DictReader(f)
    rows = list(reader)

GBK 那个坑顺带说一下

老系统导出的中文文件可能是 GBK。我测了 GBK 写的中文文件,'中文测试' 4 个字占 8 字节。用 utf-8 去读会报:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in position 0

报错信息里那个 0xd6 就是 GBK 的中文字节特征。处理办法:先用 utf-8-sig 读,失败再退到 gbk,或者从源头统一编码,别让程序去猜。

判断文件有没有 BOM 的快速办法

看头三个字节:

raw = open('data.csv', 'rb').read(3)
has_bom = raw == b'\xef\xbb\xbf'

或者读第一行再 lstrip('\ufeff') 兜一手。但注意这只是补救,正确做法还是选对编码。

一句话

Windows 写 CSV 记得 newline='' 避免 \r\r\n 双翻译空行;遇到第一列多不可见字符,用 utf-8-sig 读剥 BOM;GBK 文件别用 utf-8 硬读。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。