分隔符、引号与换行三者互斥
CSV 没有真正的数据类型,只有一条约定:字段之间用分隔符隔开,行之间用换行隔开。当字段内容本身含分隔符、双引号或换行时,必须给整个字段加双引号,字段内的双引号再翻倍。这条规则来自 RFC 4180,是各家实现勉强能互通的最大公约数;超出它的写法(自定义分隔符、不转义引号)就进入各解析器自行约定的地带。
基本信息
| 分隔符 | RFC 4180 指定逗号;分号、制表符、竖线在各地导出中同样常见 |
|---|---|
| 换行 | 标准规定 CRLF,实践里 LF 被广泛接受,导出时以 LF 即可 |
| 引号规则 | 字段含分隔符或引号或换行时整体加双引号,内部双引号写两遍 |
| 空格处理 | 字段首尾空格属于内容,不应自动去除,只有加引号才能可靠保留 |
| 编码 | 标准未规定别名编码要求;含中文且要用 Excel 打开时通常需要 BOM |
典型使用场景
- 导出报表给业务方用 Excel 打开,中文与长数字最易出问题
- 数据仓库批量导入,字段里出现逗号导致列错位
- 导出手机号、身份证号、邮编等纯数字串,需要防前导零丢失
- 导入用户提供的表格,需先确认分隔符与引号约定
- 排查「多出一列」的故障:定位首个含未转义引号的行
正确写法与工具用法
正确写法与工具用法
# 原文:他说 "你好",然后走了(含逗号与引号)
# 正确 CSV 行:
"id1","他说 ""你好"",然后走了",北京
# 用标准库生成,不要手工拼接
import csv, sys
with open('out.csv', 'w', newline='', encoding='utf-8-sig') as f:
w = csv.writer(f, quoting=csv.QUOTE_MINIMAL)
w.writerow(['id1', '他说 "你好",然后走了', '北京'])
# 读取时让解析器决定引号边界
rows = list(csv.reader(open('out.csv', encoding='utf-8-sig')))冲突类型与处理
| 冲突 | 后果 | 处理 |
|---|---|---|
| 字段含逗号 | 列错位,后续字段全部右移 | 整体加双引号 |
| 字段含双引号 | 引号配对错乱,解析器抛错或吞内容 | 双引号写两遍 |
| 字段含换行 | 一条记录被拆成两行 | 整体加双引号,按记录而非按行计数 |
| 字段以 = + - @ 开头 | 电子表格中可能被当公式执行 | 加引号并前置单引号或空格 |
| 长数字串 | 被当数值处理,前导零丢失或变科学计数 | 加引号,导入时指定为文本列 |
公式注入不是显示问题:单元格内容以等号开头且被表格软件执行时,可能触发外部请求或本地命令。导出面向人工打开的 CSV 时,对以 =、+、-、@ 开头的字段做转义处理。
Excel 在部分区域设置下用分号做分隔符,且不会自动识别 UTF-8 无 BOM 文件的中文。跨团队交付 CSV 前,确认对方环境的列表分隔符与编码,必要时同时提供制表符分隔版本。