Python CSV 列错位怎么办?检测 DictReader 中的多余列和缺失列

プログラミング・Web開発カテゴリを表すパンダのイラスト 编程・Web开发

关于本文
本文是通过利用生成式 AI 的自动化流程创建的。参考了 Python 官方的 csv 模块规范,整理了使用虚拟 CSV 查找列数不一致的步骤。

验证状态:📘 已确认官方规范,文中代码尚未在实机上运行

Python CSV 列错位怎么办?检测 DictReader 中的多余列和缺失列

如果将 CSV 作为字典加载,便无需记住列号,即可通过“商品代码”、“数量”等名称获取对应的值。然而,如果合作伙伴发来的文件中混入了多余的逗号,会发生什么情况呢?即使在屏幕上看起来能正常读取,数量也可能会被存入另一列中。更麻烦的是,读取处理在没有抛出异常的情况下继续执行,直到后续流程中才首次发现数据不一致。

Python 的csv.DictReader并不一定会将列数与表头不符的行判定为错误。它具备将多余的值和缺失的值放到哪里的机制。这次我们不使用实际的客户数据,仅在字符串内部进行复现,并进一步确认读取后立即隔离异常行的方法。结果示例是根据官方规范预测的,此处并未对代码进行实机运行。

目标环境与本次目标

项目条件
语言Python 3 系列,标准库 csv 和 io
准备无需安装额外的包、创建文件或进行网络通信
输入虚拟的商品代码与数量
验证范围参考 Python 官方规范。所附代码未执行
目标检测标题与列数不符(过多或过少)的情况并标出行号

首先,梳理一下什么是“CSV的列”。假设首行为code,qty,那么数据行应当包含两个字段。然而,A01,3,urgent有三个,而B02只有一个。这里关键的一点并不是单纯去数逗号有多少个。用引号括起来的"東京,本社"即使包含逗号也算作一个字段。如果仅仅通过字符串分割来检查CSV,连正常的行也会被误判。

初次实验:多余的列会消失吗

接下来的代码使用io.StringIO将字符串当作文件处理。不会对实际文件进行读写。首先,故意混入多余的列和缺少的列。

import csv
import io

sample = "code,qty\nA01,3,urgent\nB02\nC03,5\n"
reader = csv.DictReader(io.StringIO(sample))
for line_number, row in enumerate(reader, start=2):
    print(line_number, row)

根据官方规范预期的结果,第二行中A01多出来的urgent不会作为普通的列名,而是会被存入对应None键的列表中。第三行的B02其qty的值将变为None。第四行的C03则正常对应两列。也就是说,多余的值不会被默默丢弃,但也不会自动抛出异常。

如果不了解这个区别row["qty"]只使用时,可能会忽略多出列的事实。相反,如果断定缺少列的None和空字符串相同,就会无法区分输入中原本没有该列,还是指定了空白。在最初的观察中,不仅要看值,还要看字典的键集合与值的类型。

为什么字典中会出现 None

DictReader用于将表头名称与各行的值进行对应。当没有名称来放入多余的值时使用restkey,当值不足时使用restval这一设置。两者的默认值虽然都是None,但作用完全不同。前者是作为多余值容器的键,后者是填充不足字段的值。

下图将一行的字段数与标题进行比较,仅表示将其存储到何处。它并不是一幅解释 CSV 字符串解析本身的图。

flowchart TD
    A["見出しの列数とデータ行の列数を比較"] --> B{"列数は一致?"}
    B -->|一致| C["各見出し名へ値を対応"]
    B -->|データが多い| D["余剰値をrestkeyのリストへ"]
    B -->|データが少ない| E["不足箇所へrestvalを設定"]

图右侧的两个分支就是本次的陷阱。因为两者都能放入字典这种格式,所以不能简单判定“生成了字典就是正常的”。根据 CSV 的规范,空白也可能是正确的数据。必须将列数不一致的检测与验证值为空白是否符合业务规则分开考虑。

1 改变条件:添加专门用于检测的标记

与其使用默认的None,不如为多出的列指定专门的键,为不足的列指定专门的值。这样可以使人更容易读懂发生了什么。

import csv
import io

sample = "code,qty\nA01,3,urgent\nB02\nC03,5\n"
reader = csv.DictReader(
    io.StringIO(sample),
    restkey="__EXTRA__",
    restval="__MISSING__"
)
for line_number, row in enumerate(reader, start=2):
    problems = []
    if "__EXTRA__" in row:
        problems.append("余分な列")
    if "__MISSING__" in row.values():
        problems.append("不足した列")
    print(line_number, problems or ["列数は一致"])

预期的判断是第二行为“多出列”、第三行为“缺少列”、第四行为“列数一致”。即使改变设置,数据本身也不会被修复。添加标记只是让输入的问题更容易被发现。这就引出了只把通过检查的行传递给后续处理的设计。

不过,此示例为了说明问题,使用了字符串作为标记。如果真实数据中可能包含__MISSING__这样的字符串作为合法值,那么仅凭值进行判断就会发生冲突。此外,如果表头本身包含__EXTRA__,也会造成混淆。如果在生产环境中进行健壮处理,可以选择为缺失值使用专门的唯一对象,或者在转换为字典之前使用csv.reader检查列数。

实务指南:先统计列数再转为字典

如果只想确保列数没有多余或缺失,csv.reader使用将标题和各行都作为数组读取并对比长度的方法会非常清晰。同时还要检查表头名称是否有重复。以下是一个使用安全虚拟数据且不采用问题行的最小示例:

import csv
import io

sample = "code,qty\nA01,3,urgent\nB02\nC03,5\n"
reader = csv.reader(io.StringIO(sample, newline=""))
header = next(reader)
if not header or len(set(header)) != len(header):
    raise ValueError("見出しが空、または重複しています")

accepted = []
rejected = []
for record_number, fields in enumerate(reader, start=2):
    if len(fields) != len(header):
        rejected.append((record_number, len(fields)))
        continue
    accepted.append(dict(zip(header, fields)))

print("採用:", accepted)
print("保留:", rejected)

根据规范,预期结果是C03这一行会被采用,而第二行和第三行会被保留(不采用)。这里使用了zip,但由于在转换前对比了长度,因此不会默默截断多余的值。同时也在一开始就阻止了表头重复。在实际文件中,记录内可能包含换行符,因此这里显示的编号是CSV记录的顺序,并不一定总是物理文件的行号。如果需要实际的读取位置,请同时检查reader.line_num。

边界条件:列数一致并不代表一定正确

这里进行一个意料之外的对比。D04,这一行即使第二个值为空,字段数也依然是两个。因此它能通过列数检查。另一方面,D04由于只有一个字段所以无法通过。“数量为空”和“数量列不存在”是两个不同的问题。如果数量是必填项,在列数检查之后,还需要qty检查它是否为空字符串。

另一个是引号问题。"E,05",7虽然在商品代码中包含逗号,但作为CSV它依然是两个字段。如果用字符串的split(",")来看,就会觉得有三个字段。正因如此,列数的确认也应使用CSV解析器的结果。如果各个合作伙伴的CSV分隔符或引号规则不同,请考虑指定delimiter或quotechar。如果字符编码不同,在作为CSV解析之前的读取阶段也可能会出现问题。

空行、表头空字符串、重复表头以及数量包含非数字的情况也是其他需要检查的对象。例如qty但three不过列数是一致的。重要的是,不要将“列数检查”等同于“CSV质量保证已完成”。此外,在实际打开文件时,请遵循Python官方文档指定newline="",并在必要时明确指定encoding。

工作中该使用哪一个

对于简单的探索,或者想在用字典处理的同时记录多余的值时,DictReader的restkey和restval非常方便。另一方面,在订单数据或库存数据等不允许列错位导入的场景中,先用csv.reader检查列数、将不匹配的行隔离后再转换为字典的方法更容易进行判断。两者的区别在于:前者是“在读取时保留信息”,后者是“不采用非法行”。

在将其纳入实际运营时,建议设计为:保留导入前的原始文件,记录搁置的条数及原因,且在存在异常时阻止进入后续流程。如果擅自补全问题行并将其作为正常数据处理,可能会导致数量或金额的错误登记。建议首先用只读的小样本确认预期结果,然后在确认了字符编码、列名、空值规则以及异常处理之后,再将其应用到实际数据中。

参考资料

文档信息

文章??
Python CSV 列错位怎么办?检测 DictReader 中的多余列和缺失列
?布日期
更新日期
来源
https://papanda925.com/?p=18519&lang=zh

?可: ?于本站?有相??利的正文及原??表,除非?有?明,可依据 CC BY 4.0 使用。本文可能包含使用生成式AI?建或??的内容。若代??有?可声明,或?接的GitHub???定了?可,?代?以??可?准。引用内容、第三方?料、?片及商?不在本?可范?内。 使用政策

标题和URL已复制