← 返回Python标准库精讲目录
← 返回学习笔记首页
专题: Python标准库精讲系统学习
关键词: Python, 标准库, csv, CSV文件, reader, writer, DictReader, DictWriter, 方言, dialect, 数据导入导出
一、CSV格式概述
CSV(Comma-Separated Values,逗号分隔值)是一种极其常见且轻量级的表格数据交换格式。它以纯文本形式存储表格数据(数字和文本),每条记录占一行,字段之间用逗号分隔。尽管格式看似简单,但在实际数据交换中扮演着桥梁角色,几乎所有的数据库系统、电子表格软件(如Excel、Google Sheets)、数据分析工具(如Pandas、R)以及各类编程语言都提供了对CSV的原生支持。
CSV格式并没有一个统一的全球标准,但业界广泛遵循 RFC 4180 规范(于2005年正式发布),该规范定义了CSV格式的标准处理方式。RFC 4180 的核心规定包括:每条记录位于单独一行,由换行符(CRLF)分隔;记录中的每个字段由逗号分隔;如果字段内容包含逗号、换行符或双引号,则该字段必须用双引号包裹;如果字段内容中包含双引号,则需使用两个连续的双引号("")进行转义;文件中的每一行应包含相同数量的字段。
尽管比JSON和XML更早出现,CSV凭借其极高的可读性和极低的解析成本,在数据科学、ETL管道、系统间数据迁移、日志导出等场景中始终活跃。Python标准库中的 csv 模块封装了RFC 4180的解析逻辑,提供了高层API以降低开发者的心智负担,是数据持久化编程的必备工具。
Python的csv模块设计思想非常清晰:将行 视为数据处理的基本单位,通过reader/writer 和DictReader/DictWriter 两套API,分别面向序列(列表/元组)和映射(字典)两种编程范式。同时引入方言(Dialect) 机制来适配各种CSV变体,使得模块具有高度灵活性。
# CSV文件的基本结构示例(data.csv)
# 每行一条记录,字段用逗号分隔
name,age,city
Alice,30,New York
Bob,25,Los Angeles
Charlie,35,"San Francisco, CA"
上述示例中,第一行为表头(header),定义了字段名称;后续行为数据行。第三行中"San Francisco, CA"包含逗号,因此需要用双引号包裹。以下是一个包含特殊字符(双引号)的CSV行示例:
# 字段内容包含双引号时的转义
id,note
1,He said "Hello"
经过规范编码后,该行变为:
1,"He said ""Hello"""
在Python中,使用csv模块读取该数据会自动完成转义还原,开发者无需手动处理这些底层细节。
二、reader与writer
csv模块提供了最基本的 csv.reader 和 csv.writer 两个函数/类,用于从CSV文件中读取行数据 和写入行数据 。它们的共同特点是操作对象为序列 (list或tuple),每一行被解析为一个列表,字段按顺序对应到列表元素。
2.1 csv.reader — 逐行读取CSV文件
csv.reader 接受一个可迭代对象(通常是打开的文件对象)作为参数,返回一个 reader 对象 。这个reader对象是一个迭代器,对其执行for循环或调用 next() 将逐行返回解析后的字段列表。Python的csv.reader内部自动处理了以下复杂情况:引号内逗号的识别、引号内换行符的识别、双引号转义的还原以及行尾逗号的处理。
import csv
# 方式一:使用for循环遍历
with open ('data.csv' , 'r' , encoding='utf-8' ) as f:
reader = csv.reader (f)
for row in reader:
print (row) # 每行是一个列表:['Alice', '30', 'New York']
# 方式二:转换为列表(小文件场景)
with open ('data.csv' , 'r' , encoding='utf-8' ) as f:
rows = list (csv.reader (f))
print (rows[0 ]) # 表头:['name', 'age', 'city']
# 方式三:使用next跳过表头
with open ('data.csv' , 'r' , encoding='utf-8' ) as f:
reader = csv.reader (f)
header = next (reader) # 获取并跳过表头行
for row in reader:
print (f"Name: {row[0]}, Age: {row[1]}" )
reader的行为特点: 返回的row始终是一个list,字段按列顺序排列。如果文件末尾包含空行,reader会自动跳过。reader不会在内存中缓存全部数据,适合处理大文件。reader支持多种参数来控制解析行为,其中最常用的是 delimiter 和 quotechar。默认的delimiter是逗号(,),默认的quotechar是双引号(")。
# 读取制表符分隔的TSV文件
with open ('data.tsv' , 'r' , encoding='utf-8' ) as f:
reader = csv.reader (f, delimiter='\t' , quotechar="'" )
for row in reader:
print (row)
2.2 csv.writer — 写入CSV文件
csv.writer 将一个可写文件对象包装为writer对象,提供了 writerow() 和 writerows() 两个核心方法。writerow() 一次写入一行(传入一个可迭代对象如列表或元组),writerows() 一次写入多行(传入一个可迭代对象的可迭代对象,如列表的列表)。writer会自动处理需要转义的情形:如果字段内容包含逗号、换行符或引号字符,writer会自动用双引号包裹字段,并将字段内部的引号双写转义。
import csv
headers = ['name' , 'age' , 'city' ]
rows = [
['Alice' , 30 , 'New York' ],
['Bob' , 25 , 'Los Angeles' ],
['Charlie' , 35 , 'San Francisco, CA' ],
]
# 写入CSV文件
with open ('output.csv' , 'w' , newline='' , encoding='utf-8' ) as f:
writer = csv.writer (f)
writer.writerow (headers) # 写入表头
writer.writerows (rows) # 写入多行数据
# 写入Windows平台文件时注意 newline='' 参数
# 这是官方文档明确推荐的做法,可以避免多余的空行
写入CSV时一个常见的陷阱是 换行符处理 。Python官方文档明确建议:在打开文件用于写入CSV时,始终指定 newline='' 参数。这是因为csv模块自己控制行结束符,如果让Python的文件对象也参与换行符转换,会导致写入的CSV文件中出现多余的空行(尤其是在Windows平台上)。
# 错误示范——缺少 newline='' 会导致多余空行
# with open('bad.csv', 'w') as f: ← 不要这样做!
# writer = csv.writer(f)
# 正确写法
with open ('good.csv' , 'w' , newline='' , encoding='utf-8' ) as f:
writer = csv.writer (f)
writer.writerow (['a' , 'b' ])
writerow与writerows的对比:
方法 参数 说明 示例
writerow() 单行可迭代对象 一次写入一行 writer.writerow(['a','b'])
writerows() 可迭代对象的可迭代对象 一次写入多行,内部循环调用writerow writer.writerows([['a','b'],['c','d']])
writerows() 在底层仍然是对每一行分别调用 writerow(),因此两者的行末处理、转义逻辑完全一致。使用 writerows() 可以减少Python级别的循环代码,使代码更加简洁。
三、DictReader与DictWriter
与基于序列的reader/writer不同,DictReader 和 DictWriter 提供了 基于字典(映射)的读写接口 。在这种模式下,每一行被表示为一个字典:键为列名(默认取自第一行表头),值为对应字段的内容。这种方式使得代码的可读性极大提升,尤其是在列数较多或列顺序可能发生变化的情况下。
3.1 DictReader — 字典形式读取
DictReader 将CSV文件的第一行自动解析为字段名列表(fieldnames) ,后续每一行转换为 OrderedDict(Python 3.6+中为普通dict),键为fieldnames中的列名,值为对应列的内容。如果CSV文件没有表头行,可以手动传入 fieldnames 参数指定列名。
import csv
# CSV文件内容:
# name,age,city
# Alice,30,New York
# Bob,25,Los Angeles
with open ('data.csv' , 'r' , encoding='utf-8' ) as f:
reader = csv.DictReader (f)
for row in reader:
print (row['name' ], row['age' ], row['city' ])
# 输出:Alice 30 New York
# 输出:Bob 25 Los Angeles
# 手动指定 fieldnames(文件没有表头时)
with open ('data_no_header.csv' , 'r' , encoding='utf-8' ) as f:
reader = csv.DictReader (f, fieldnames=['name' , 'age' , 'city' ])
for row in reader:
print (row['name' ])
重要提示: DictReader的行类型在Python 3.6+中为普通的 dict(保持了插入顺序),在更早版本中为 OrderedDict。如果使用手动指定fieldnames,第一行数据不会被当作表头跳过,而是作为普通数据行按fieldnames映射。
3.2 DictWriter — 字典形式写入
DictWriter 接受一个 fieldnames 参数(必填),定义了字典的键与CSV列之间的映射关系。写入时调用 writerow() 传入字典,DictWriter会根据fieldnames提取对应值并按顺序写出。如果字典中缺少某个fieldnames中定义的键,则该字段写出空字符串;如果字典中包含fieldnames之外的键,这些键会被忽略。writeheader() 方法可以将fieldnames作为表头行写入文件。
import csv
fieldnames = ['name' , 'age' , 'city' ]
rows = [
{'name' : 'Alice' , 'age' : '30' , 'city' : 'New York' },
{'name' : 'Bob' , 'age' : '25' , 'city' : 'Los Angeles' },
{'name' : 'Charlie' , 'age' : '35' , 'city' : 'San Francisco, CA' },
]
with open ('output.csv' , 'w' , newline='' , encoding='utf-8' ) as f:
writer = csv.DictWriter (f, fieldnames=fieldnames)
writer.writeheader () # 写入表头:name,age,city
writer.writerows (rows) # 写入多行数据
# 使用extrasaction参数控制多余列的处理
# 默认 extrasaction='raise',遇到不认识的键会抛 ValueError
# 设置为 'ignore' 则静默忽略
with open ('strict.csv' , 'w' , newline='' ) as f:
writer = csv.DictWriter (f, fieldnames=['name' , 'age' ], extrasaction='ignore' )
writer.writeheader ()
writer.writerow ({'name' : 'Alice' , 'age' : '30' , 'extra' : 'ignored' })
DictWriter的extrasaction参数: 当传入的字典包含fieldnames中没有的键时,默认行为是抛出 ValueError 异常。这是一个保护机制,可以防止因列名拼写错误导致数据静默丢失。如果确实需要忽略多余的键,设置 extrasaction='ignore'。
总结:DictReader/DictWriter将CSV的列与字典的键建立了显式映射,虽然带来轻微性能开销(字典查找),但极大地提高了代码的可维护性和可读性。在列数较多、列顺序可能变化或需要选择性读写的场景下,强烈推荐使用字典接口。
四、方言Dialect
CSV格式虽然在概念上统一为"逗号分隔值",但在实际应用中存在大量变体:不同的操作系统使用不同的行结束符、不同的区域设置使用不同的分隔符(欧洲国家常用分号代替逗号)、不同的应用程序使用不同的引号规则……为了应对这种多样性,csv模块引入了 方言(Dialect) 的概念。方言是一组命名参数的集合,封装了CSV的格式设置,使得参数的复用变得简单。
4.1 标准方言
csv模块内置了三个标准方言:
方言 delimiter quotechar quoting lineterminator 说明
excel, " QUOTE_MINIMAL \r\n 默认方言,兼容Excel导出的CSV
excel-tab\t " QUOTE_MINIMAL \r\n 制表符分隔的Excel兼容格式
unix, " QUOTE_ALL \n 类Unix系统风格,所有字段都加引号
# 使用内置方言
import csv
# 使用 excel 方言读取
with open ('data.csv' , 'r' , encoding='utf-8' ) as f:
reader = csv.reader (f, dialect='excel' )
for row in reader:
print (row)
# 使用 excel-tab 方言读取制表符分隔文件
with open ('data.tsv' , 'r' , encoding='utf-8' ) as f:
reader = csv.reader (f, dialect='excel-tab' )
# 使用 unix 方言写入
with open ('data_unix.csv' , 'w' , newline='' , encoding='utf-8' ) as f:
writer = csv.writer (f, dialect='unix' )
writer.writerow (['a' , 'b' ])
4.2 自定义方言
通过继承 csv.Dialect 类可以定义自己的方言,或使用 csv.register_dialect() 函数注册一个命名方言。自定义方言的核心属性包括:
属性 默认值 说明
delimiter , 字段分隔符,单字符
quotechar " 引号字符,单字符
quoting QUOTE_MINIMAL 引号使用策略
escapechar None 转义字符,设为\\可用反斜杠转义
lineterminator \r\n 行结束符(writer使用)
skipinitialspace False 是否跳过分隔符后的空白
doublequote True 引号内双引号是否双写转义
strict False 是否严格解析,设为True时遇到错误抛出异常
# 方式一:继承Dialect类
class MyDialect (csv.Dialect):
delimiter = '|'
quotechar = "'"
quoting = csv.QUOTE_MINIMAL
escapechar = None
lineterminator = '\n'
skipinitialspace = True
doublequote = True
strict = False
csv.register_dialect ('myformat' , MyDialect)
with open ('data.psv' , 'r' , encoding='utf-8' ) as f:
reader = csv.reader (f, dialect='myformat' )
for row in reader:
print (row)
# 方式二:直接使用参数,不显式注册方言
# 效果等价于一次性使用的匿名方言
with open ('data.psv' , 'r' , encoding='utf-8' ) as f:
reader = csv.reader (f, delimiter='|' , quotechar="'" , skipinitialspace=True )
4.3 Sniffer嗅探器
在实际项目中经常遇到这种情况:拿到一个CSV文件但不知道它的方言配置(分隔符是什么、是否有表头、引用字符是什么)。csv模块提供了 csv.Sniffer 类来解决这个问题,它可以自动嗅探 (分析)CSV文件的方言。
import csv
with open ('unknown_format.csv' , 'r' , encoding='utf-8' ) as f:
sample = f.read (1024 ) # 读取前1024字节作为样本
dialect = csv.Sniffer().sniff (sample)
print (f"分隔符: {dialect.delimiter}" )
print (f"引号符: {dialect.quotechar}" )
# Sniffer还可以判断是否有表头
has_header = csv.Sniffer().has_header (sample)
print (f"是否有表头: {has_header}" )
# 嗅探后直接使用自动识别的方言读取
f.seek (0 ) # 回到文件开头
reader = csv.reader (f, dialect=dialect)
for row in reader:
print (row)
Sniffer使用提示: Sniffer基于统计分析来推断方言,样本数据量越大、越有代表性,嗅探结果越准确。建议至少提供几百字节的样本。Sniffer在处理非常规分隔符(如有多个候选分隔符且它们在数据中出现频率相近)时可能误判,此时需要人工介入确认。
五、引号控制
csv模块通过 quoting 参数提供了四种引号控制策略,决定了writer在何时对字段值加引号,以及reader如何解释引号。这些策略定义在csv模块的四个常量中:QUOTE_ALL、QUOTE_MINIMAL、QUOTE_NONNUMERIC 和 QUOTE_NONE。
5.1 QUOTE_MINIMAL(默认)
只在必要时 才加引号。具体来说,仅当字段包含以下字符之一时进行包裹:分隔符(默认逗号)、quotechar、换行符(\n或\r\n)或行结束符。这是最节省空间的策略,也是Excel的默认行为。适用于绝大多数通用场景。
# QUOTE_MINIMAL — 默认行为,仅在必要时加引号
import csv
rows = [['normal' , 'has, comma' , 'has "quote"' , 'has\nnewline' ]]
with open ('minimal.csv' , 'w' , newline='' ) as f:
writer = csv.writer (f, quoting=csv.QUOTE_MINIMAL)
writer.writerows (rows)
# 输出:normal,"has, comma","has ""quote""","has
# newline"
5.2 QUOTE_ALL
对所有字段都加引号,无论字段内容是否包含特殊字符。这种方式生成的CSV文件最为统一和可预测,但文件体积会增大。在某些严格要求数据格式一致的系统中,QUOTE_ALL是推荐的策略。
# QUOTE_ALL — 所有字段都加引号
with open ('all_quoted.csv' , 'w' , newline='' ) as f:
writer = csv.writer (f, quoting=csv.QUOTE_ALL)
writer.writerow (['Alice' , '30' , 'New York' ])
# 输出:"Alice","30","New York"
5.3 QUOTE_NONNUMERIC
对reader和writer的行为不同:
writer场景: 所有非数字类型的字段(即无法通过float()转换的字段)都会被加引号。但这个行为在Python中难以精确判定(所有字段传入时都是字符串),因此实际效果与QUOTE_ALL相近。
reader场景: 所有未加引号的字段会被自动转换为 float 类型。这在读取数值密集的CSV文件时非常有用,但需要注意文本字段如果未被加引号也会被转换为float,可能导致ValueError。
# QUOTE_NONNUMERIC — reader:未引号字段自动转float
# CSV内容:
# "Alice",30,"New York"
# "Bob","25","Los Angeles"
with open ('mixed.csv' , 'r' ) as f:
reader = csv.reader (f, quoting=csv.QUOTE_NONNUMERIC)
for row in reader:
print (type (row[1 ])) # 未加引号的 30 会变成 float(30.0)
# 加引号的字段 "25" 会作为字符串保留
5.4 QUOTE_NONE
从不对任何字段加引号。如果字段内容中包含分隔符、换行符或quotechar字符,writer会抛出异常(除非同时设置了 escapechar 用于转义)。reader场景中,字段内的引号被当作普通字符处理。这种模式适用于已经预处理好字段内容的场景,或者与escapechar配合使用。
# QUOTE_NONE + escapechar — 用反斜杠转义特殊字符
with open ('escaped.csv' , 'w' , newline='' ) as f:
writer = csv.writer (f, quoting=csv.QUOTE_NONE, escapechar='\\' )
writer.writerow (['hello' , 'a,b' ])
# 输出:hello,a\,b
四种引号策略对比汇总:
策略 值 writer行为 reader行为
QUOTE_MINIMAL 0 仅特殊字符时加引号 按引号原样解析
QUOTE_ALL 1 所有字段加引号 去掉所有引号
QUOTE_NONNUMERIC 2 非数字字段加引号 未加引号的转float
QUOTE_NONE 3 从不加引号(需escapechar) 引号当作普通字符
六、错误处理
csv模块在解析过程中可能遇到各种格式问题,主要通过 csv.Error 异常类来汇报。csv.Error是 Exception 的直接子类,在解析不规范CSV文件时抛出。所有csv模块产生的异常都是 csv.Error 类型或它的子类。
6.1 常见异常场景
以下情形会触发 csv.Error:
字段数不一致: 默认情况下csv模块不会因为某行字段数与其他行不同而抛出异常,每行独立解析。但 strict=True 模式下会抛出 Error。
引号不匹配: CSV行中引号没有成对闭合时,reader会尝试跨行匹配(这是RFC 4180允许的行为),但如果不期望这种行为且strict=True,则会报错。
QUOTE_NONE模式下遇到引号: 如果没有设置escapechar,特殊字符无法转义会抛出Error。
编码问题: 编码错误通常由 open() 抛出 UnicodeDecodeError,而不是csv.Error。需要在外层捕获。
import csv
def safe_read_csv (filepath):
try :
with open (filepath, 'r' , encoding='utf-8' ) as f:
reader = csv.reader (f)
for i, row in enumerate (reader, 1 ):
print (f"第 {i} 行: {row}" )
except csv.Error as e:
print (f"CSV解析错误(第 {i} 行附近): {e}" )
except UnicodeDecodeError as e:
print (f"文件编码错误: {e}" )
except FileNotFoundError:
print (f"文件不存在: {filepath}" )
# 使用strict模式检测格式异常
def strict_read_csv (filepath):
try :
with open (filepath, 'r' , encoding='utf-8' ) as f:
reader = csv.reader (f, strict=True )
return list (reader)
except csv.Error as e:
print (f"CSV格式不严格符合规范: {e}" )
return None
6.2 编码问题处理
CSV文件最常见的问题之一是编码不一致 。不同系统导出的CSV可能使用不同的编码(UTF-8、GBK/GB2312、Latin-1等)。Python的csv模块并不处理编码——编码解包由 open() 的文件对象负责。因此编码错误会在文件读取层抛出,而不是csv.Error。实际项目中推荐的做法是:先尝试UTF-8,失败后回退到其他编码。
import csv
def open_csv_with_fallback (filepath):
"""尝试多种编码打开CSV文件"""
encodings = ['utf-8' , 'gbk' , 'gb2312' , 'latin-1' ]
for enc in encodings:
try :
return open (filepath, 'r' , encoding=enc)
except UnicodeDecodeError:
continue
raise ValueError (f"无法识别文件编码: {filepath}" )
# 使用回退编码打开CSV
with open_csv_with_fallback ('unknown_encoding.csv' ) as f:
reader = csv.reader (f)
for row in reader:
print (row)
最佳实践: 处理CSV文件时,始终将csv逻辑包裹在try/except块中,并区分csv.Error(格式问题)和IOError/FileNotFoundError(文件路径问题)以及UnicodeDecodeError(编码问题)。对于大规模数据处理(超过10万行),建议在遍历reader时添加行号计数和异常捕获,这样即使某行解析失败也能精准定位,而不至于整个任务崩溃。
七、实战案例与总结
本章通过几个贴近真实工作场景的案例,展示csv模块的综合应用。这些案例覆盖了从基础读写到进阶技巧的各个方面。
7.1 案例一:大文件分块处理
在处理超大CSV文件(如超过1GB的日志文件)时,不能一次性将整个文件读入内存。正确做法是使用reader迭代器逐行处理,并结合分块(chunk)思想进行批量写入——这本质上正是csv.reader的设计意图:作为迭代器,它始终只保留当前行在内存中。
import csv
def process_large_csv (input_path, output_path, chunk_size=10000 ):
"""大文件CSV处理:读取、转换、写入"""
with open (input_path, 'r' , encoding='utf-8' ) as fin, \
open (output_path, 'w' , newline='' , encoding='utf-8' ) as fout:
reader = csv.reader (fin)
writer = csv.writer (fout)
header = next (reader)
writer.writerow (header) # 原样写入表头
processed = 0
chunk = []
for row in reader:
# 对每行进行转换处理(示例:将第二列转为大写)
row[1 ] = row[1 ].upper ()
chunk.append (row)
processed += 1
if len (chunk) >= chunk_size:
writer.writerows (chunk)
chunk = []
print (f"已处理 {processed} 行..." )
# 处理剩余行
if chunk:
writer.writerows (chunk)
print (f"处理完成,共 {processed} 行" )
7.2 案例二:CSV数据清洗
现实中的CSV数据往往不规整:包含空白字符、空值表示为不同形式(空字符串、"N/A"、"null")、数值含有千分位分隔符等。数据清洗是CSV处理中最常见的任务。
import csv
def clean_csv (input_path, output_path):
"""清洗CSV数据:去除空白、统一空值、转换数值"""
with open (input_path, 'r' , encoding='utf-8' ) as fin, \
open (output_path, 'w' , newline='' , encoding='utf-8' ) as fout:
reader = csv.DictReader (fin)
fieldnames = reader.fieldnames
writer = csv.DictWriter (fout, fieldnames=fieldnames)
writer.writeheader ()
for row in reader:
cleaned = {}
for key, value in row.items ():
if value is None :
cleaned[key] = ''
else :
value = value.strip ()
if value.lower () in ('n/a' , 'null' , 'none' , '-' ):
cleaned[key] = ''
else :
# 去除数值中的千分位逗号
if ',' in value and value.replace (',' , '' ).isdigit ():
value = value.replace (',' , '' )
cleaned[key] = value
writer.writerow (cleaned)
7.3 案例三:CSV编码转换
跨系统数据交换时常遇到编码问题:Windows中文系统默认使用GBK编码导出CSV,而现代系统更倾向于UTF-8。下面演示如何实现编码转换。
import csv
def convert_csv_encoding (input_path, output_path, src_enc='gbk' , dst_enc='utf-8' ):
"""CSV文件编码转换"""
with open (input_path, 'r' , encoding=src_enc) as fin, \
open (output_path, 'w' , newline='' , encoding=dst_enc) as fout:
reader = csv.reader (fin)
writer = csv.writer (fout)
for row in reader:
writer.writerow (row)
print (f"编码转换完成:{src_enc} → {dst_enc}" )
# 使用示例:将GBK编码的CSV转为UTF-8
# convert_csv_encoding('chinese_data.csv', 'chinese_data_utf8.csv')
7.4 总结:csv模块核心要点
Python的csv模块虽然小巧,但设计精良、功能完备。归纳其核心要点如下:
类别 核心API 适用场景
基础读写 reader / writer 简单场景、逐行处理大文件
字典读写 DictReader / DictWriter 列名可读性要求高、列数多、列序可变
方言控制 Dialect / register_dialect 非标准分隔符、跨系统兼容
自动检测 Sniffer 未知格式CSV文件
引号策略 QUOTE_* 常量 精确控制CSV输出格式
错误处理 csv.Error 稳定健壮的生产级代码
核心心得: 处理CSV时始终坚持以下原则:(1) 始终使用 newline='' 打开写入文件;(2) 优先使用 with 语句确保文件关闭;(3) 明确定义编码,避免依赖系统默认编码;(4) 大文件使用迭代器而非一次性读取;(5) 用DictReader/DictWriter减少按索引取值的脆弱性。掌握这些要点后,Python的csv模块足以覆盖95%以上的CSV文件处理需求。