Python 内置 csv 模块:核心类与基本读写

处理 CSV 文件,Python 标准库自带的 csv 模块其实就够了。我早年刚入行时,也试过自己写字符串分割逻辑,结果被各种引号、换行符、逗号嵌套搞到崩溃。后来老老实实用内置模块,再没出过解析事故。

说白了,csv 模块提供了四个核心类:reader、writer、DictReader、DictWriter。前两个处理列表形式的数据,后两个处理字典形式的数据。咱们一个一个来看。

reader:逐行读取,返回列表

csv.reader 是最基础的读取方式。它把每一行解析成一个列表,字段按顺序排列。

import csv

with open('recipes.csv', 'r', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)  # 每行是一个列表

输出大概是这样的:

['配方编号', '原料名称', '用量', '单位']
['R001', '面粉', '500', 'g']
['R001', '水', '300', 'ml']

嗯,这里要注意:reader 默认把第一行也当成普通数据。如果你想把第一行当表头,需要手动调用 next() 跳过。

我的习惯:只要 CSV 有表头,我几乎不用 reader,而是直接用 DictReader。代码更干净,也不容易搞错字段顺序。

DictReader:按列名读取,返回字典

DictReader 是我个人最常用的类。它自动把第一行作为字段名,后续每行返回一个字典,键就是表头。

import csv

with open('recipes.csv', 'r', encoding='utf-8') as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row['配方编号'], row['原料名称'], row['用量'])

输出:

R001 面粉 500
R001 水 300

你看,代码可读性一下子高了很多。我在项目中处理配方数据时,几乎清一色用 DictReader。为什么?因为配方表经常有几十个字段,用列表下标去取数据,写起来累,读起来更累。

我曾经踩过的坑:CSV 文件里如果有多余的空格,比如表头写成 "配方编号 "(后面多了个空格),DictReader 会把空格也当成键的一部分。后来我养成了习惯,读取前先检查表头,或者用 strip() 清洗一下。

writer:逐行写入,接收列表

写 CSV 文件时,csv.writer 是最直接的方式。它接收列表,一行一行地写进去。

import csv

with open('output.csv', 'w', encoding='utf-8', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['配方编号', '原料名称', '用量', '单位'])
    writer.writerow(['R002', '糖', '200', 'g'])
    writer.writerow(['R002', '鸡蛋', '3', '个'])

这里有个细节:newline='' 必须加上。如果不加,Windows 系统下每行后面会多一个空行。我当年第一次写 CSV 时就被这个坑过,输出文件打开一看,行与行之间全是空行,尴尬得很。

DictWriter:按字典写入,自动对齐列

DictWriter 和 DictReader 是对称的。它需要你提前指定字段名列表,写入时自动按顺序对齐。

import csv

fieldnames = ['配方编号', '原料名称', '用量', '单位']

with open('output.csv', 'w', encoding='utf-8', newline='') as f:
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()  # 写入表头
    writer.writerow({'配方编号': 'R002', '原料名称': '糖', '用量': '200', '单位': 'g'})
    writer.writerow({'配方编号': 'R002', '原料名称': '鸡蛋', '用量': '3', '单位': '个'})

你想想看,如果字段有 20 个,用 writer 写列表你得记住每个位置对应什么。用 DictWriter 就省心多了,字典的键名一目了然,顺序由 fieldnames 统一控制。

四个核心类的对比

类名 输入/输出格式 是否自动处理表头 适用场景
reader 列表 否 简单读取,字段少且固定
DictReader 字典 是(第一行作为键) 字段多、表头变化、需要按列名访问
writer 列表 否 快速写入,字段顺序明确
DictWriter 字典 是(需调用 writeheader) 字段多、需要保证列顺序、数据来自字典

知识结构图:csv 模块核心脉络

下面这张图帮你理清四个类的关系和选择路径:

csv 模块核心类选择流程图 csv 模块核心类选择路径 开始处理 CSV 读 / 写? 读取 写入 reader(列表) DictReader(字典) writer(列表) DictWriter(字典) 选择原则:字段少且固定 → reader/writer;字段多或表头变化 → DictReader/DictWriter 我个人的经验:配方数据校验场景,90% 的情况用 DictReader + DictWriter 组合

基本读写操作:几个实用技巧

除了上面四个类的用法,还有几个操作细节值得记住。

指定分隔符

CSV 不一定是逗号分隔。我遇到过用制表符、分号甚至竖线做分隔符的文件。这时可以指定 delimiter 参数:

reader = csv.reader(f, delimiter='\t')  # 制表符分隔
writer = csv.writer(f, delimiter=';')   # 分号分隔

处理引号嵌套

如果字段本身包含逗号或换行符,CSV 会用引号包裹。内置模块自动处理这些情况,你不需要额外操心。

# 假设 CSV 中有一行:R001,"面粉,高筋",500,g
# reader 会自动解析为:['R001', '面粉,高筋', '500', 'g']

写入时控制引号行为

quoting 参数可以控制什么时候加引号:

参数值 含义 常用场景
csv.QUOTE_MINIMAL 只在必要时加引号(默认) 通用场景
csv.QUOTE_ALL 所有字段都加引号 数据可能包含特殊字符时
csv.QUOTE_NONNUMERIC 非数字字段加引号 需要区分类型时
csv.QUOTE_NONE 从不加引号 数据绝对干净时(不推荐)
核心要点:内置 csv 模块虽然轻量,但足够应对 90% 的日常 CSV 处理需求。四个类中,DictReader 和 DictWriter 是配方数据校验场景的主力。记住加 newline=''、注意表头空格、善用 delimiter 参数,基本就不会出大问题。