用 Python csv.DictReader 校验表格:区分空值、缺失值和多余列

举报
yd_210400787 发表于 2026/11/08 01:21:00 2026/11/08
【摘要】 用 Python 标准库 csv.DictReader 读取 CSV,明确区分空字段、缺少字段和多余字段,并校验表头与年龄,附可运行示例。

CSV 常被用来交换用户或商品清单。它看起来只是逗号分隔的文本,但读取时有三个不同的问题:city 留空、整行少了一个字段、整行多了一个字段。若把它们统统当成空字符串,后续数据处理就可能悄悄吞掉格式错误。

下面用 Python 标准库 csv.DictReader 做一个小型校验器。示例要求表头严格为 name,age,city;name 必填,age 是 0~120 的整数,city 可以是空字符串。

完整代码

保存为 csv_people.py:

"""Read a small CSV file with explicit column and value checks."""

from __future__ import annotations

import csv
import sys
from pathlib import Path


def read_people(path: Path) -> list[dict[str, str | int]]:
    people: list[dict[str, str | int]] = []
    with path.open("r", encoding="utf-8-sig", newline="") as source:
        reader = csv.DictReader(source, restkey="__extra__", restval=None)
        expected = ["name", "age", "city"]
        if reader.fieldnames != expected:
            raise ValueError(f"表头应为 {expected},实际为 {reader.fieldnames}")

        for row in reader:
            line = reader.line_num
            if "__extra__" in row:
                raise ValueError(f"第 {line} 行有多余的列:{row['__extra__']}")
            missing = [key for key in expected if row[key] is None]
            if missing:
                raise ValueError(f"第 {line} 行缺少列值:{missing}")
            if not row["name"].strip():
                raise ValueError(f"第 {line} 行 name 不能为空")
            try:
                age = int(row["age"])
            except ValueError as error:
                raise ValueError(f"第 {line} 行 age 必须是整数") from error
            if not 0 <= age <= 120:
                raise ValueError(f"第 {line} 行 age 超出 0~120")
            people.append({"name": row["name"], "age": age, "city": row["city"]})
    return people


if __name__ == "__main__":
    for person in read_people(Path(sys.argv[1])):
        print(person)

准备 people.csv:

name,age,city
Alice,28,
Bob,0,Shanghai

运行 python csv_people.py people.csv,得到:

{'name': 'Alice', 'age': 28, 'city': ''}
{'name': 'Bob', 'age': 0, 'city': 'Shanghai'}

Alice 的 city 是空字符串,表示第三列存在,只是没有填写。若把该行改成 Alice,28,DictReader 会用 restval=None 补齐 city;校验器便报“缺少列值”。若改成 Alice,28,,extra,多出来的值会放进 __extra__,也会报错。

为什么这样读取

reader.fieldnames 与预期列表做精确比较,可以同时发现列顺序不对、缺列、多列以及重复表头。如果业务允许列顺序变化,可以比较列名集合,但仍需单独检查重复名称。

newline="" 让 csv 模块自己处理换行,避免带引号的多行字段或不同系统的换行方式被提前改写。encoding="utf-8-sig" 可读取普通 UTF-8 文件,也会去掉文件开头的 UTF-8 BOM;否则首列表头可能变成带 BOM 的 \ufeffname。

reader.line_num 是读完当前记录后的物理行号。普通单行记录中,它就是出错行;如果字段里包含被引号包住的换行,它指向该记录结束的那一行,定位时要留意这一点。

使用边界

CSV 中的值初始都是文本,age 需要明确转换和范围检查。示例把空 city 视为有效;实际业务若要求城市必填,应增加相应校验。这个示例只适合小文件,因为它把所有结果收集到列表;处理大文件时,可以把函数改成生成器,逐条交给后续流程。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。