Python读取CSV基础:使用csv模块的reader()、逐行读取与类型转换、处理表头与数据分离
说实话,很多人一上来就用Pandas处理CSV。但我觉得,先搞懂Python自带的csv模块,才是真正的「基本功」。你想想看,如果连最底层的读取逻辑都不清楚,出了问题你怎么排查?
我在项目里遇到过好几次——Pandas读出来全是乱码,或者数据类型全变成object。这时候回头用csv.reader()一查,问题立马就暴露了。所以这一章,咱们就把根基打牢。
2.1 为什么选csv模块?
Python标准库里的csv模块,轻量、稳定、零依赖。它不像Pandas那样「全家桶」,但恰恰因为简单,你才能看清每一行数据是怎么进来的。
| 对比项 | csv模块 | Pandas |
|---|---|---|
| 依赖 | 无(标准库) | 需安装 |
| 读取速度(小文件) | 快 | 稍慢(加载开销) |
| 内存占用 | 低(逐行) | 高(全量加载) |
| 类型推断 | 无(全字符串) | 自动推断 |
| 适用场景 | 调试、小文件、流式处理 | 数据分析、大表操作 |
说白了,csv模块就是给你一把「手术刀」,让你精确控制每一行。而Pandas是「瑞士军刀」,功能多但有时候会自作主张。
2.2 reader() 的核心用法
csv.reader()接受一个文件对象,返回一个迭代器。每次迭代返回一行,类型是列表。嗯,这里要注意——它不会自动跳过空行,也不会处理引号内的换行,这些都得你自己兜底。
import csv
with open('配方数据.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
print(row)
这段代码看起来简单,但我踩过一个坑:如果CSV文件是Excel导出的,编码可能是gbk或gb2312。用utf-8读会直接报错。我建议你第一次打开时,用try-except兜一下编码问题。
chardet库自动检测也行,但小文件我直接试utf-8和gbk两种。
2.3 逐行读取与类型转换
reader()返回的每一行都是字符串列表。但配方数据里,温度、浓度、时间这些字段,你肯定希望它们是数字。所以类型转换这一步,必须手动做。
import csv
with open('配方数据.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
# row[0] 是配方名称,保持字符串
name = row[0]
# row[1] 是温度,转成float
temp = float(row[1])
# row[2] 是浓度,转成float
conc = float(row[2])
# row[3] 是批次号,保持字符串
batch = row[3]
print(f'{name}: {temp}°C, {conc}%, 批次{batch}')
为什么会这样?因为CSV本质是纯文本,没有类型信息。你想想看,Excel打开CSV时能自动识别数字,那是因为它做了「猜测」。但Python不会替你猜,它把选择权交给你。
float('')会抛ValueError。后来我加了个判断:temp = float(row[1]) if row[1] else None。记住,生产环境里「脏数据」才是常态。
2.4 处理表头与数据分离
大多数CSV文件第一行是表头。我见过有人直接next(reader)跳过,然后遍历剩下的行。这没问题,但有个细节——表头行本身也可能包含空值或注释,你得先校验一下。
import csv
with open('配方数据.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
header = next(reader)
# 校验表头是否完整
expected = ['配方名称', '温度', '浓度', '批次号']
if header != expected:
print(f'警告:表头不匹配,期望{expected},实际{header}')
# 正式读取数据
for row in reader:
# 跳过空行
if not any(row):
continue
name, temp, conc, batch = row
# 类型转换
temp = float(temp) if temp else None
conc = float(conc) if conc else None
print(f'{name}: {temp}, {conc}, {batch}')
我个人习惯把表头和数据分离后,用dict来组织每一行。这样代码可读性更高,也不容易搞错列索引。
import csv
with open('配方数据.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
header = next(reader)
data = []
for row in reader:
if not any(row):
continue
record = dict(zip(header, row))
# 类型转换
record['温度'] = float(record['温度']) if record['温度'] else None
record['浓度'] = float(record['浓度']) if record['浓度'] else None
data.append(record)
# 现在 data 是一个字典列表,用字段名访问
for rec in data:
print(rec['配方名称'], rec['温度'])
2.5 本章知识结构图
下面这张图,帮你把本章的核心逻辑串起来。从文件打开到数据可用,每一步都有对应的处理要点。
2.6 常见问题与避坑
- 编码错误:用
encoding='utf-8-sig'可以自动处理BOM头。Excel导出的UTF-8文件经常带BOM。 - 引号内换行:如果某个字段包含换行符,
reader()会自动处理,但前提是字段被引号包裹。如果没包裹,行数会错乱。 - 空行:CSV文件末尾经常有空行。用
if not any(row): continue跳过。 - 列数不一致:某一行少了几列,
reader()不会报错,但后续索引会越界。我建议每行都检查len(row)。
csv.DictReader可以自动把第一行当表头,返回字典。但它的类型转换还是得手动做。我个人更喜欢用reader() + zip(header, row),更灵活。
好了,这一章的内容就这些。记住:csv.reader()是基础,逐行读取是核心,类型转换是必须,表头分离是规范。把这四点吃透,后面批量导入配方数据时,你就能游刃有余了。