Python 读取 CSV:正确处理逗号、换行与 UTF-8 BOM
处理导出的数据表时,最容易写出的代码是逐行读取,再用 split(‘,’) 切分字段。它对简单样例有效,但备注里一旦出现逗号,列就会错位。本文用一个小例子说明如何用 Python 标准库读取 CSV,并在读取入口检查数据结构。示例是教学用虚拟数据,不涉及真实业务记录。
1. 先看一条容易误读的数据
下面的 CSV 有三列,第二条记录的备注包含逗号,第三条记录的备注包含换行。双引号包住的内容仍然属于一个字段:
name,quantity,note
apple,2,"fresh, red"
pear,3,"line one
line two"
对第二条记录直接 split(‘,’) 会得到四段;按物理行分别处理第三条记录,又会把同一条数据拆开。CSV 的记录边界与文本换行并不总是一回事,因此应让解析器处理引号和分隔符。
2. 用 DictReader 按列名读取
将上面的数据保存为 data.csv,再运行以下脚本:
import csv
with open('data.csv', encoding='utf-8-sig', newline='') as f:
reader = csv.DictReader(f)
expected = ['name', 'quantity', 'note']
if reader.fieldnames != expected:
raise ValueError(f'表头不符合预期: {reader.fieldnames!r}')
total = 0
for record_no, row in enumerate(reader, start=1):
if None in row or any(v is None for v in row.values()):
raise ValueError(f'第 {record_no} 条记录列数不匹配')
try:
quantity = int(row['quantity'])
except ValueError as exc:
raise ValueError(f'第 {record_no} 条记录数量不是整数') from exc
if quantity < 0:
raise ValueError(f'第 {record_no} 条记录数量不能为负数')
total += quantity
print(row['name'], quantity, repr(row['note']))
print('合计:', total)
这份示例的数量合计应为 5。repr() 会把备注中的换行显示为转义形式,方便观察字段是否完整。这里的 record_no 是数据记录序号,并非文件物理行号。
3. 两个参数各管一件事
newline=‘’ 让 csv 模块自行处理换行,尤其是带引号字段内的换行。encoding=‘utf-8-sig’ 在读取时可去除文件开头的 UTF-8 BOM,也能读取不带 BOM 的 UTF-8 文件。它不是通用编码探测器;如果来源明确为 GBK,应使用对应编码,不能靠忽略解码错误来“修好”数据。
4. 在入口发现结构问题
DictReader 负责解析,不负责判断业务值是否合理。示例显式检查表头、字段数、整数格式与非负约束,是为了让错误尽早暴露。表头顺序检查是本例的数据约定;若业务允许列顺序变化,应改为核对列名集合,同时检查重复列名。空字符串也不等于缺列,需要按字段规则分别处理。
对于大文件,可以保持逐条处理,只累计必要结果,不必先把 reader 转成列表。小脚本一旦接入定时任务,更应让异常清楚说明问题位置,避免悄悄吞掉坏数据。
参考:
- Python 官方文档:《csv — CSV File Reading and Writing》
- Python 官方文档:《codecs — Codec Registry and Base Classes》
本文由 AI 辅助编写,示例用于解释 CSV 解析与校验方法。
- 点赞
- 收藏
- 关注作者
评论(0)