Python 3 在 Windows 写 CSV,每两条记录之间多出空行
Python 3 CSV 默认行结束符与 Windows 文本换行转换叠加,可能产生额外空行。通过 newline 空字符串修正写入,核对字节与 CSV 记录并保留原文件。
csv.writer 或 csv.DictWriter 写出的文件,在表格软件里每条记录后面都多了一行空白,原始数据却没有空记录。使用默认文本模式打开输出文件时,Windows 上的换行转换可能与 CSV 自己的行结束符叠加,产生 \r\r\n。
下面针对 Python 3 标准库 csv,尤其是 open(path, 'w') 生成的文件。它不涵盖程序主动写入空记录、字段内部合法换行或第三方导出工具的其他问题。
在 open 中设置 newline=''
CSV 默认 writer 使用 \r\n 结束记录。文本文件默认的 newline=None 又会把写入的 \n 转为系统行分隔符;Windows 上便可能额外多出一个 \r。
让 CSV 模块负责换行,在打开文件时设置空字符串 newline='':
import csv
rows = [
{"name": "alpha", "quantity": 2},
{"name": "beta", "quantity": 3},
]
with open("export-new.csv", "x", encoding="utf-8", newline="") as output:
writer = csv.DictWriter(output, fieldnames=["name", "quantity"])
writer.writeheader()
writer.writerows(rows)
示例使用 Python 3.3 起支持的独占创建模式 x,已有同名文件时会失败,避免覆盖旧导出。实际程序若采用 w,会截断已有文件,应先确认路径和备份。修复的关键是 newline='',并不是必须把所有输出模式都改成 x。
这里的值是空字符串,不是一个空格。不要把 Python 2 时代的 wb 写法直接套进 Python 3 的普通 CSV writer:writer 输出文本,需要支持文本写入的文件对象。字符编码与换行是两个设置;修改 encoding 不能代替 newline。
看原始字节,区分额外回车和真实空记录
在小型、无敏感内容的样例上,可查看开头的一段字节:
with open("export-new.csv", "rb") as source:
print(repr(source.read(200)))
对于前面的样例,记录结束处应是 \r\n,不应因输出层再次翻译而出现 \r\r\n。二进制读取仅用于观察文件,不会再替换换行字符。
不要把“文件末尾有一个换行”当成同一故障。CSV writer 正常结束最后一条记录也会写行结束符,这不要求删除最后一个字符。真正需要检查的是记录间是否新增了空记录,或是否出现重复回车。
用 CSV reader 核对记录,而不是按物理行计数
CSV 字段允许包含被引号包住的换行,一条记录可以占多行。可以按相同编码和 CSV 规则重新读取刚生成的样例:
import csv
with open("export-new.csv", "r", encoding="utf-8", newline="") as source:
records = list(csv.reader(source))
assert records == [
["name", "quantity"],
["alpha", "2"],
["beta", "3"],
]
这段检查仅适合前面的短样例,既验证记录数,也验证字段内容。大文件应逐条核对,避免为了排错一次性读入所有数据。
如果设置 newline='' 后仍有空记录,检查传给 writerows() 的序列是否包含空列表,以及循环是否额外调用了 write('\n') 或写了另一份空记录。若只是在某个单元格中显示多行,则检查字段本身的换行与导入设置。
不要用替换全文换行来修导出
把 \r 从整个文件中删除,可能改动字段中的真实内容;把 writer 的 lineterminator 随意改成单独 \r,也可能改变下游要求的格式。需要特殊方言时再明确约定,不要把它当作默认的空行修复。
修正写入端后,保留旧文件,重新导出到新路径,再用实际下游程序确认字段、记录数量和中文内容。回退代码不会还原已经覆盖的导出文件,因此切换前应保留原件;也不要把新导出追加到旧的错误文件里,否则旧空行和重复记录仍会留在结果中。


