当前位置: 代码网 > it编程>前端脚本>Python > Python读写大CSV文件的实战指南

Python读写大CSV文件的实战指南

2026年09月11日 Python 我要评论
处理 csv 看起来是 python 里最没技术含量的活:open 一下按逗号 split 不就完了?直到你的 csv 里某个字段本身带逗号(比如地址「北京市, 朝阳区」),或者带换行、带引号,spl

处理 csv 看起来是 python 里最没技术含量的活:open 一下按逗号 split 不就完了?直到你的 csv 里某个字段本身带逗号(比如地址「北京市, 朝阳区」),或者带换行、带引号,split(',') 立刻把一行切成好几段,数据全乱。再或者用 excel 打开你生成的 csv,中文全是乱码;又或者读一个 2gb 的文件,一个 read() 直接把内存吃爆。

这些坑标准库 csv 模块基本都替你处理了,但你得知道怎么用对。这篇把 csv 读写里最高频的三类问题——引号转义、编码/bom、大文件流式处理——一次讲透。

别用 split(‘,’) 手撕 csv

先看为什么手写解析必翻车。假设有这么一行数据,地址字段里带了逗号:

1,张三,"北京市, 朝阳区",28

按 csv 规范,带逗号的字段要用双引号包起来。你要是 line.split(','):

line = '1,张三,"北京市, 朝阳区",28'
print(line.split(','))
# ['1', '张三', '"北京市', ' 朝阳区"', '28']  ← 5 段!地址被切成两半,引号还留着

彻底错了。而 csv 模块知道引号规则:

import csv, io

line = '1,张三,"北京市, 朝阳区",28'
reader = csv.reader(io.stringio(line))
print(next(reader))
# ['1', '张三', '北京市, 朝阳区', '28']  ← 正确,4 段,引号也去掉了

csv 的转义规则比想象中复杂:字段里有逗号、换行、双引号时要用双引号包裹,字段内的双引号要写成两个双引号 ""。这些规则手写迟早出错,csv 模块全处理好了。结论:永远别用字符串切割解析 csv,用 csv 模块。

读:用 dictreader 按列名取,别数下标

基础读法是 csv.reader,返回每行的列表,靠下标取值:

import csv

with open('users.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)
    header = next(reader)              # 第一行是表头
    for row in reader:
        print(row[1], row[2])         # 靠下标 1、2 取,可读性差、易错

下标法有个大问题:列一多、或者上游调整了列顺序,row[2] 到底是啥全靠数,极易错位。用 dictreader 按列名取:

with open('users.csv', newline='', encoding='utf-8') as f:
    reader = csv.dictreader(f)         # 自动把第一行当表头
    for row in reader:
        print(row['name'], row['city'])   # 按列名取,清晰且抗列顺序变化

注意那个 newline=''——这是最容易漏又最坑的参数。打开 csv 文件给 csv 模块用时,必须传 newline=''。因为 csv 模块要自己处理行内的 \r\n(字段里可能包含换行),如果不传,python 的通用换行转换会和 csv 的处理打架,在 windows 上会每行之间多出一个空行,或者字段内换行被�is错误处理。记死:csv 读写,open 一律带 newline=''

写:字段带逗号/换行,交给 writer 自动加引号

写 csv 同理,别自己拼字符串。csv.writer 会自动判断哪些字段需要加引号:

import csv

rows = [
    ['id', 'name', 'address'],
    [1, '张三', '北京市, 朝阳区'],        # 带逗号
    [2, '李四', '含"引号"的名字'],         # 带引号
    [3, '王五', '第一行\n第二行'],          # 带换行
]

with open('out.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.writer(f)
    writer.writerows(rows)

生成的文件里,带特殊字符的字段被自动正确转义:

id,name,address
1,张三,"北京市, 朝阳区"
2,李四,"含""引号""的名字"
3,王五,"第一行
第二行"

注意第 2 行的引号被转义成了 "",第 3 行的字段因为含换行被整体用引号包住——这些都是 csv.writer 自动做的,手写绝对会漏。

写字典数据用 dictwriter,还能顺手用 writeheader() 写表头:

with open('out.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.dictwriter(f, fieldnames=['id', 'name', 'address'])
    writer.writeheader()
    writer.writerow({'id': 1, 'name': '张三', 'address': '北京市, 朝阳区'})

dictwriter 有个好处:如果 dict 里的 key 和 fieldnames 对不上(多了个 key),会直接抛 valueerror,帮你抓住数据里的脏字段,而不是静默漏写。

编码坑:excel 打开中文乱码,加 bom

你用 utf-8 写的 csv,自己 python 读没问题,但同事用 excel(尤其是 windows 版)双击打开,中文全是乱码。这是因为 excel 在没有明确编码标记时,默认按系统本地编码(简中环境是 gbk)去猜,把 utf-8 的中文认成了乱码。

解法是用 utf-8-sig 编码写——它会在文件开头写一个 bom(字节顺序标记),excel 看到 bom 就知道「这是 utf-8」,正确显示:

# 给 excel 用的 csv,用 utf-8-sig 加 bom
with open('for_excel.csv', 'w', newline='', encoding='utf-8-sig') as f:
    writer = csv.writer(f)
    writer.writerow(['姓名', '城市'])
    writer.writerow(['张三', '北京'])

反过来,读一个带 bom 的文件时,也用 utf-8-sig,它会自动吃掉 bom。如果你用普通 utf-8 读带 bom 的文件,第一个字段名会莫名多个  前缀:

# 用普通 utf-8 读带 bom 的文件:表头第一个 key 会带 
with open('for_excel.csv', encoding='utf-8') as f:
    reader = csv.dictreader(f)
    print(reader.fieldnames)   # ['姓名', '城市']  ← 第一个 key 脏了!

# 正确:用 utf-8-sig 读,自动去 bom
with open('for_excel.csv', encoding='utf-8-sig') as f:
    reader = csv.dictreader(f)
    print(reader.fieldnames)   # ['姓名', '城市']

这个  前缀极其隐蔽:row['姓名'] 会 keyerror,但打印出来看着一模一样,排查半天。规则:和 excel 打交道就用 utf-8-sig,读写都用它最省心。

大文件:逐行流式处理,别一次读进内存

处理几百 mb 甚至几 gb 的 csv,新手常见错误是 f.read()list(reader) 把整个文件读进内存,直接 oom。

好消息是 csv.reader 本身就是惰性迭代器——你 for 循环它时,它一次只读一行,内存占用和文件大小无关:

import csv

# 正确:逐行迭代,内存恒定,几 gb 也不怕
def count_by_city(path):
    from collections import counter
    counter = counter()
    with open(path, newline='', encoding='utf-8') as f:
        reader = csv.dictreader(f)
        for row in reader:              # 一次一行,不会把整个文件读进内存
            counter[row['city']] += 1
    return counter

反例(别这么写):

with open('huge.csv', newline='', encoding='utf-8') as f:
    rows = list(csv.dictreader(f))      # ← 整个文件塞进内存,大文件直接 oom
    for row in rows:
        ...

只要你不主动 list() 它、不 append 到一个大列表里,for 循环逐行处理就是流式的。想边读边写(比如清洗大文件),同时开读和写两个流,处理一行写一行:

def clean_csv(src, dst):
    with open(src, newline='', encoding='utf-8') as fin, \
         open(dst, 'w', newline='', encoding='utf-8-sig') as fout:
        reader = csv.dictreader(fin)
        writer = csv.dictwriter(fout, fieldnames=reader.fieldnames)
        writer.writeheader()
        for row in reader:
            row['name'] = row['name'].strip()   # 逐行清洗
            if row['city']:                      # 过滤掉没城市的
                writer.writerow(row)
    # 全程内存只驻留一行,处理 10gb 文件也稳

一个隐藏坑:超大字段报 field larger than field limit

如果某个字段特别大(比如一整段 json、base64 内容塞进一个单元格),读的时候可能报:

_csv.error: field larger than field limit (131072)

csv 模块默认单字段上限约 128kb。确实有超大字段就调大限制:

import csv, sys

csv.field_size_limit(sys.maxsize)   # 放开单字段大小限制

但先想清楚:字段真有必要这么大吗?通常这种超大字段是数据设计有问题(该拆表或存文件路径),放开限制是权宜之计。

小结

  • 永远用 csv 模块,别 split(','):带逗号、换行、引号的字段,手写切割必翻车,csv 按规范自动处理转义。
  • 读用 dictreader 按列名取(抗列顺序变化),写用 dictwriter + writeheader();open一律带 newline='',否则 windows 上多空行、字段内换行出错。
  • 编码:和 excel 打交道用 utf-8-sig(写加 bom 防乱码,读自动去 bom);用普通 utf-8 读带 bom 文件,第一个列名会藏个  导致 keyerror。
  • 大文件:csv.reader/dictreader 本身是惰性迭代器,for 循环逐行处理内存恒定;别 list(reader)f.read() 一次读进内存;边读边写同时开两个流。
  • 超大字段报 field larger than field limit 时用 csv.field_size_limit(sys.maxsize) 放开,但先反思字段该不该这么大。

一句话记忆:csv 三件套记牢——解析交给 csv 模块、opennewline=''、跟 excel 打交道用 utf-8-sig;大文件靠迭代器天然流式,别手jian list() 它。

以上就是python读写大csv文件的实战指南的详细内容,更多关于python读写大csv文件的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com