Python 读写 CSV:编码、换行和字段边界要分别处理

10-01 5阅读

CSV 看起来像“逗号分隔的文本”,但直接 split 逗号很快会遇到带引号的地址、跨行备注和编码问题。可靠处理需要分清三层:字节用什么编码解码,CSV 方言怎样描述字段,业务如何解释字段值。本文基于 Python 3.14 标准库,示例输入是已经确认采用 UTF-8、可能带 BOM 的受信任 CSV。

Python 读写 CSV:编码、换行和字段边界要分别处理

AI生成概念配图:文本记录经解析后形成边界清楚的表格字段。仅辅助理解,不代表真实界面或实测结果。

让文本层与 CSV 层各司其职

import csv

with open('input.csv', encoding='utf-8-sig', newline='') as f:
    reader = csv.DictReader(f)
    expected = ['id', 'name', 'note']
    if reader.fieldnames != expected:
        raise ValueError('Unexpected columns')
    for row in reader:
        if None in row or any(v is None for v in row.values()):
            raise ValueError('Wrong field count')
        print(row['id'], row['name'])

newline 空字符串让 csv 模块按自己的规则处理换行,能正确配合字段内部换行与写出行结束符。utf-8-sig 解码会去掉开头的 UTF-8 BOM,也能读取不带 BOM 的 UTF-8 内容;它不是自动识别所有中文编码。已知文件来自其他编码时,应明确使用约定值,并保留原始文件用于核对。

不要用 errors="ignore" 让解码错误悄悄消失。字段中少几个字符可能影响姓名、标识或金额,后续逻辑却仍正常运行。遇到错误应先确认文件来源和编码契约,再转换到新的文件;不要一边猜编码,一边覆盖唯一原件。

把字段校验写在导入入口

DictReader 默认把首行作为列名,并通常返回字符串。代码显式检查列名与顺序,防止多出一列、少一列或 BOM 残留导致错位;同时检查字段数异常。真实系统还应检查重复列名、必填值、长度和业务范围,并把错误记录定位到可核查的位置。跨行字段存在时,物理行号与业务记录序号并不相同。

订单号、邮编和设备编号可能带前导零,不应因为“看起来像数字”就转成整数。金额应按业务约定使用合适的十进制处理,日期要明确格式与时区。空字符串与缺失字段也不是同一个概念,需要在导入契约中分别定义,避免把数据清洗写成无法追溯的猜测。

导出时交给 writer 处理引用

import csv

rows = [
    {'id': '0012', 'name': '示例用户', 'note': '包含逗号,以及\n第二行'}
]
with open('output.csv', 'w', encoding='utf-8', newline='') as f:
    writer = csv.DictWriter(f, fieldnames=['id', 'name', 'note'])
    writer.writeheader()
    writer.writerows(rows)

writer 会根据方言为需要的字段加引号,并处理字段中的双引号;不要自己先把每个字段用引号包好,否则可能重复转义。示例生成 UTF-8 输出,是否需要 BOM 应由接收工具的导入规则决定,而不是一律添加。还要约定分隔符,因为扩展名 .csv 并不能保证每个生产者都使用逗号。

文件能解析,不代表表格软件会按原样展示

将 CSV 交给电子表格软件后,编号可能被自动转成数字,日期可能被按地区设置解释,某些以公式符号开头的外部输入可能被当作公式。CSV 引号主要保护字段边界,并不能阻止这些语义解释。对于不可信数据,需按目标工具采用明确的文本导入与安全导出策略,不能只依赖 quoting 设置。

验收时用相同约定重新读取导出文件,比较字段值而不是肉眼数逗号,并准备中文、BOM、引号、逗号、跨行字段、前导零和空值样本。若处理大文件,可以逐条验证和写出;若要求整批原子交付,则先写临时结果、校验通过后再按受控流程发布,避免消费者读到半份文件。

参考资料

文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。