DevCN
菜单
全部文章快讯开发科技深度热点

Python CSV 处理:读取带逗号的标题并逐行报告错误

读取包含引号和逗号的学习记录,使用 DictReader 验证表头、转换时长并报告错误行,避免手工 split 造成列错位。


本节目标

读取包含引号和逗号的学习记录,使用 DictReader 验证表头、转换时长并报告错误行,避免手工 split 造成列错位。

知识与使用场景

CSV 看起来像用逗号分开的文本,但字段自身也可能包含逗号、引号和换行。本节标题“函数,参数”是一个字段,不能用 split(',') 拆分。标准库 csv 会按照格式规则处理引号。

本例固定表头为 title 和 minutes,数值列读出来仍是字符串,需要显式转换。清洗规则为标题非空、时长为非负整数;错误行加入错误列表,合法行继续参与统计。这与上一节严格失败的生成器不同,体现的是导入策略选择。

示例用 StringIO 保存一份内存 CSV,方便直接运行。换成磁盘文件时使用 open(path, encoding='utf-8', newline='');若文件确实带 UTF-8 BOM,可使用 utf-8-sig,避免 BOM 混入第一列表头。

完整示例

import csv
from io import StringIO

text = 'title,minutes\n"函数,参数",25\n循环,30\n错误记录,abc\n'
reader = csv.DictReader(StringIO(text, newline=""))
if reader.fieldnames != ["title", "minutes"]:
    raise ValueError("表头必须是title,minutes")

records = []
errors = []
for row in reader:
    line = reader.line_num
    try:
        if None in row or any(value is None for value in row.values()):
            raise ValueError("列数不匹配")
        title = row["title"].strip()
        minutes = int(row["minutes"])
        if not title or minutes < 0:
            raise ValueError("标题为空或时长为负")
    except ValueError:
        errors.append(f"第{line}行:列数、标题或时长无效")
        continue
    records.append({"title": title, "minutes": minutes})

print(records[0]["title"])
print(f"有效:{len(records)},合计:{sum(r['minutes'] for r in records)}")
print(errors[0])

预期结果

函数,参数
有效:2,合计:55
第4行:列数、标题或时长无效

运行与逐步理解

运行后首行完整输出“函数,参数”,说明引号内逗号没有拆成新列。两条合法记录合计 55 分钟,第三条数据不能转换成整数,因此留下第 4 行错误信息;表头也占一行。

DictReader 遇到多余列时会使用 None 键收集,缺列时可能产生 None 值,所以示例先检查列数,再访问和转换字段。没有这一步,格式不齐的文件可能被默默忽略多余内容。

reader.line_num 是已读取的物理行号。如果某个带引号字段跨多行,它指向该记录读取结束的位置,不一定是记录开始行。本例每条记录只有一行,因此可以直接与文本位置对应。

常见问题与边界

  • 不要默认所有 CSV 都使用逗号;实际导入应明确分隔符和编码。
  • 示例只处理行级校验错误;严重的 CSV 格式错误应终止导入并提示修复文件。
  • 错误被收集不代表数据已导入成功,页面应分别展示成功数和失败数。
  • 导出给电子表格时,要另行处理可能被当作公式的外部文本。

动手练习

把错误记录的时长改为 15,再增加一行“空时长,”,预测有效条数、总分钟及错误位置。

参考答案

前 3 条数据变为合法记录,共计 70 分钟;新增第 5 行的空时长无法转换成整数,被记录为错误。因此有效条数为 3,失败条数为 1。

DISCUSSION

文章回复

0 条公开回复
未登录回复需要审核后公开
还没有回复,欢迎参与讨论。