Python 读写 CSV 文件实战

Python 读写 CSV 文件实战:纯文本时代的「通用表格协议」

1. CSV 文件:轻量级跨平台数据中转站

CSV(Comma Separated Values),全称「逗号分隔值」,是一个没有官方强制标准、但业界已有广泛共识的纯文本数据格式。在 Daoman Python AI 的实际项目中,CSV 几乎无处不在:

  • 数据库的临时导入/导出中间件
  • 前后端、跨语言系统间的「数据快递」
  • 中小规模机器学习数据集的快速存储与交换

它最大的优势就是纯文本:不管是用 Windows 记事本、macOS Numbers、Linux Vim 还是 Excel,都能直接打开、直接编辑,完全不需要安装特殊的解析工具。

大家默认遵循的 CSV 结构约定:

  1. 纯文本,编码友好:常用 UTF-8(跨平台首选),有时也遇到 GBK(例如国内部分 Excel 导出的文件)
  2. 每一行是一条完整记录:比如一行就是“关羽的三门课成绩”
  3. 字段分隔清晰:默认用英文逗号 ,,但也可以使用制表符 \t、竖线 | 等作为分隔符(当数据内部本身就含有大量逗号时尤其有用)
  4. 表头可选,但强烈推荐:第一行写明每一列的含义,让人和机器都能一目了然
  5. 特殊内容用包围符:如果某个字段里包含了分隔符、换行符或引号本身,就用双引号 " 把整个字段包起来,避免解析错乱

2. 用原生 csv 模块写入文件:从简单数据到自定义格式

Python 标准库自带 csv 模块,无需 pip 安装任何第三方包,就能覆盖日常约 80% 的 CSV 读写需求。我们先从最基础的场景开始:把关羽、张飞等五位蜀汉将领的随机成绩存成 scores.csv

基础场景:普通数据 + UTF-8 编码

import csv
import random

# ✅ 必须养成的习惯:
# 1. open 时用 newline='',避免 Windows 平台产生多余空行
# 2. 明确指定 encoding='utf-8',防止中文乱码
with open('scores.csv', 'w', encoding='utf-8', newline='') as f:
    # 创建 writer 写入器
    writer = csv.writer(f)
    
    # 先写入表头,方便后续阅读和处理
    writer.writerow(['姓名', '语文', '数学', '英语'])
    
    # 生成模拟数据并逐行写入
    five_tigers = ['关羽', '张飞', '赵云', '马超', '黄忠']
    for name in five_tigers:
        # 列表推导式快速生成 3 门 50~100 的随机成绩
        scores = [random.randint(50, 101) for _ in range(3)]
        # 把姓名插到成绩列表最前面
        scores.insert(0, name)
        writer.writerow(scores)

✨ 运行后用 Excel 打开试试?记得在导入时选择「UTF-8 逗号分隔」格式,中文就能正常显示了。

进阶场景:自定义分隔符 + 包围符

实际工作中,你可能会碰到一些“非主流”的 CSV 文件:

  • 用竖线 | 分隔(因为数据中天然包含大量逗号)
  • 所有字段都强制加双引号(一种更安全、无歧义的通用做法)

这时,只需给 csv.writer 传入额外参数即可:

import csv
import random

with open('custom_scores.csv', 'w', encoding='utf-8', newline='') as f:
    # 🎯 自定义参数:
    # delimiter='|' → 使用竖线作为字段分隔符
    # quoting=csv.QUOTE_ALL → 所有字段都加上双引号保护
    writer = csv.writer(
        f,
        delimiter='|',
        quoting=csv.QUOTE_ALL
    )
    
    writer.writerow(['姓名', '备注', '语文', '数学', '英语'])
    five_tigers = ['关羽', '张飞', '赵云', '马超', '黄忠']
    notes = ['桃园结义二弟', '桃园结义三弟', '长坂坡英雄', '锦马超', '百步穿杨']
    for name, note in zip(five_tigers, notes):
        scores = [random.randint(50, 101) for _ in range(3)]
        row = [name, note] + scores
        writer.writerow(row)

查看生成的文件,你会发现每一列都用 | 隔开,并且所有字段都被双引号包裹,既清晰又安全。


3. 用原生 csv 模块读取文件:从逐行遍历到键值对访问

读取 CSV 时,有两种最常用的方式:列表式读取(简单直接)和字典式读取(强烈推荐,自动关联表头与数据)。

方式一:列表式读取(csv.reader

import csv

with open('scores.csv', 'r', encoding='utf-8') as f:
    # 创建 reader 迭代器(注意:迭代器只能完整遍历一次!)
    reader = csv.reader(f)
    for row in reader:
        # row 是一个字符串列表,无论原始数据是数字还是中文
        print(f"第 {reader.line_num} 行数据:", row)

方式二:字典式读取(csv.DictReader,强烈推荐!)

如果你不想靠索引 row[0] 取姓名、靠 row[1] 取语文成绩,DictReader 会是更好的选择——它自动将表头作为字典的键,每一行数据转换为一个字典:

import csv

with open('scores.csv', 'r', encoding='utf-8') as f:
    # 创建 DictReader 迭代器
    reader = csv.DictReader(f)
    # 打印表头信息
    print("表头:", reader.fieldnames)
    print("-" * 40)
    for row in reader:
        # 直接通过键名取数据,语义清晰、不易出错
        print(f"{row['姓名']}的成绩:语文{row['语文']},数学{row['数学']},英语{row['英语']}")

📌 小提示:如果读取的是前面定义的竖线分隔文件,记得在创建 readerDictReader 时同样传入 delimiter='|',否则解析会失败哦!


4. 实战避坑指南(新手必看!)

csv 模块看似简单,但实际使用中仍有几个高频踩坑点,提前了解能省下大量调试时间。

坑1:中文乱码

  • 原因:编码不一致。例如用 GBK 写入却用 UTF-8 读取,或反过来。
  • 解决:写入和读取时,始终显式指定相同的 encoding。国内 Excel 默认常用 GBK,其他场景建议优先使用 UTF-8。

坑2:Windows 平台多余空行

  • 原因:Windows 默认换行符为 \r\n,而 csv 模块会自动处理换行,不需要 open 函数自带的行尾转换。
  • 解决:写入文件时,open() 必须加上 newline=''(读取时一般也要加,保持一致性)。

坑3:迭代器只能遍历一次

  • 原因csv.readercsv.DictReader 返回的是迭代器,不支持重复遍历。
  • 解决:如果需要多次使用数据,可以用 list(reader) 将全部内容转为列表。但要注意,大文件(几十万行以上)最好还是逐行处理,避免内存爆炸。

5. 总结与前瞻:原生 csv 是基础,Pandas 是进阶武器

原生 csv 模块适用场景

  • 中小规模 CSV 文件(几万行以内)的快速读写
  • 需要高度自定义分隔符、包围符等格式的场景
  • 不希望引入第三方依赖的轻量级项目

进阶神器 Pandas 的威力

Daoman Python AI 涉及的大规模数据分析、机器学习场景中,我们几乎不再手写原生 csv,而是直接使用 Pandas

  1. 一行代码读写 CSVpd.read_csv()df.to_csv(),简洁明了
  2. 自动转换为 DataFrame:一种类似 Excel 的二维表结构,支持筛选、排序、分组聚合、缺失值处理等高级操作,一行 API 就能替代原生几十行代码
  3. 性能优势显著:对于几十万甚至上百万行的文件,Pandas 读取更快、内存管理更优

下一篇文章,我们将正式迈入 Pandas 读写 CSV 的世界,体验“核武器”级别的数据处理效率。敬请期待!