csv — CSV 文件读取和写入

源代码: Lib/csv.py


所谓的 CSV(逗号分隔值)格式是电子表格和数据库最常用的导入和导出格式。在 RFC 4180 中尝试以标准化方式描述该格式之前,CSV 格式已经使用了多年。缺乏明确的标准意味着不同应用程序生成和使用的数据中经常存在细微的差异。这些差异使得处理来自多个来源的 CSV 文件很麻烦。尽管分隔符和引号字符各不相同,但总体格式足够相似,因此可以编写一个可以高效操作此类数据的单个模块,从而向程序员隐藏读取和写入数据的细节。

csv 模块实现了以 CSV 格式读取和写入表格数据的类。它允许程序员说“以 Excel 首选的格式写入此数据”或“从 Excel 生成的此文件中读取数据”,而无需知道 Excel 使用的 CSV 格式的精确细节。程序员还可以描述其他应用程序理解的 CSV 格式或定义他们自己的专用 CSV 格式。

csv 模块的 readerwriter 对象读取和写入序列。程序员还可以使用 DictReaderDictWriter 类以字典形式读取和写入数据。

另请参阅

PEP 305 - CSV 文件 API

提议将此添加到 Python 的 Python 增强提案。

模块内容

csv 模块定义了以下函数

csv.reader(csvfile, dialect='excel', **fmtparams)

返回一个 reader 对象,该对象将处理给定 csvfile 中的行。 csvfile 必须是字符串的可迭代对象,每个字符串都采用 reader 定义的 csv 格式。 csvfile 通常是类文件对象或列表。如果 csvfile 是文件对象,则应使用 newline='' 打开。[1] 可以提供可选的 dialect 参数,该参数用于定义特定于特定 CSV 方言的一组参数。它可以是 Dialect 类的子类的实例,也可以是 list_dialects() 函数返回的字符串之一。可以提供其他可选的 fmtparams 关键字参数来覆盖当前方言中的各个格式化参数。有关方言和格式化参数的完整详细信息,请参阅 方言和格式化参数 部分。

从 csv 文件读取的每一行都作为字符串列表返回。除非指定了 QUOTE_NONNUMERIC 格式选项(在这种情况下,未加引号的字段将转换为浮点数),否则不会执行自动数据类型转换。

一个简短的用法示例

>>> import csv
>>> with open('eggs.csv', newline='') as csvfile:
...     spamreader = csv.reader(csvfile, delimiter=' ', quotechar='|')
...     for row in spamreader:
...         print(', '.join(row))
Spam, Spam, Spam, Spam, Spam, Baked Beans
Spam, Lovely Spam, Wonderful Spam
csv.writer(csvfile, dialect='excel', **fmtparams)

返回一个 writer 对象,该对象负责将用户的数据转换为给定类文件对象上的分隔字符串。 csvfile 可以是任何具有 write() 方法的对象。如果 csvfile 是文件对象,则应使用 newline='' 打开[1]。可以提供可选的 dialect 参数,该参数用于定义特定于特定 CSV 方言的一组参数。它可以是 Dialect 类的子类的实例,也可以是 list_dialects() 函数返回的字符串之一。可以提供其他可选的 fmtparams 关键字参数来覆盖当前方言中的各个格式化参数。有关方言和格式化参数的完整详细信息,请参阅 方言和格式化参数 部分。为了尽可能方便地与实现 DB API 的模块进行交互,值 None 被写入为空字符串。虽然这不是可逆的转换,但这使得将 SQL NULL 数据值转储到 CSV 文件中更容易,而无需预处理从 cursor.fetch* 调用返回的数据。所有其他非字符串数据在写入之前都会使用 str() 进行字符串化。

一个简短的用法示例

import csv
with open('eggs.csv', 'w', newline='') as csvfile:
    spamwriter = csv.writer(csvfile, delimiter=' ',
                            quotechar='|', quoting=csv.QUOTE_MINIMAL)
    spamwriter.writerow(['Spam'] * 5 + ['Baked Beans'])
    spamwriter.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])
csv.register_dialect(name[, dialect[, **fmtparams]])

dialectname 关联。 name 必须是字符串。可以通过传递 Dialect 的子类、或通过 fmtparams 关键字参数,或两者都指定方言,其中关键字参数会覆盖方言的参数。有关方言和格式化参数的完整详细信息,请参阅 方言和格式化参数 部分。

csv.unregister_dialect(name)

从方言注册表中删除与 name 关联的方言。如果 name 不是注册的方言名称,则会引发 Error

csv.get_dialect(name)

返回与name关联的方言。如果 name 不是已注册的方言名称,则会引发 Error 异常。此函数返回一个不可变的 Dialect

csv.list_dialects()

返回所有已注册方言的名称。

csv.field_size_limit([new_limit])

返回解析器允许的当前最大字段大小。如果给定 new_limit,则此值将成为新的限制。

csv 模块定义了以下类

class csv.DictReader(f, fieldnames=None, restkey=None, restval=None, dialect='excel', *args, **kwds)

创建一个行为类似于常规读取器的对象,但将每行中的信息映射到一个 dict,其键由可选的 fieldnames 参数给出。

fieldnames 参数是一个 序列。如果省略 fieldnames,则文件 f 的第一行的值将用作字段名,并且将从结果中省略。如果提供了 fieldnames,则将使用它们,并且第一行将包含在结果中。无论如何确定字段名,字典都会保留其原始顺序。

如果某行的字段数多于字段名,则剩余数据将放入列表中,并使用 restkey 指定的字段名存储(默认为 None)。如果非空白行的字段数少于字段名,则缺失的值将用 restval 的值填充(默认为 None)。

所有其他可选或关键字参数都将传递给底层的 reader 实例。

如果传递给 fieldnames 的参数是一个迭代器,它将被强制转换为一个 list

在 3.6 版本中更改: 现在返回的行类型为 OrderedDict

在 3.8 版本中更改: 现在返回的行类型为 dict

一个简短的用法示例

>>> import csv
>>> with open('names.csv', newline='') as csvfile:
...     reader = csv.DictReader(csvfile)
...     for row in reader:
...         print(row['first_name'], row['last_name'])
...
Eric Idle
John Cleese

>>> print(row)
{'first_name': 'John', 'last_name': 'Cleese'}
class csv.DictWriter(f, fieldnames, restval='', extrasaction='raise', dialect='excel', *args, **kwds)

创建一个行为类似于常规写入器的对象,但将字典映射到输出行。fieldnames 参数是一个 sequence 的键,用于标识传递给 writerow() 方法的字典中值的写入文件 f 的顺序。可选的 restval 参数指定如果字典在 fieldnames 中缺少键要写入的值。如果传递给 writerow() 方法的字典包含在 fieldnames 中找不到的键,则可选的 extrasaction 参数指示要采取的操作。如果设置为 'raise'(默认值),则会引发 ValueError 异常。如果设置为 'ignore',则会忽略字典中的额外值。任何其他可选或关键字参数都将传递给底层的 writer 实例。

请注意,与 DictReader 类不同,DictWriter 类的 fieldnames 参数不是可选的。

如果传递给 fieldnames 的参数是一个迭代器,它将被强制转换为一个 list

一个简短的用法示例

import csv

with open('names.csv', 'w', newline='') as csvfile:
    fieldnames = ['first_name', 'last_name']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

    writer.writeheader()
    writer.writerow({'first_name': 'Baked', 'last_name': 'Beans'})
    writer.writerow({'first_name': 'Lovely', 'last_name': 'Spam'})
    writer.writerow({'first_name': 'Wonderful', 'last_name': 'Spam'})
class csv.Dialect

Dialect 类是一个容器类,其属性包含有关如何处理双引号、空格、分隔符等的信息。由于缺乏严格的 CSV 规范,不同的应用程序会产生略有不同的 CSV 数据。Dialect 实例定义了 readerwriter 实例的行为方式。

所有可用的 Dialect 名称都由 list_dialects() 返回,并且可以通过其初始化器 (__init__) 函数将其注册到特定的 readerwriter 类,如下所示

import csv

with open('students.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile, dialect='unix')
class csv.excel

excel 类定义了 Excel 生成的 CSV 文件的常用属性。它以方言名称 'excel' 注册。

class csv.excel_tab

excel_tab 类定义了 Excel 生成的制表符分隔文件的常用属性。它以方言名称 'excel-tab' 注册。

class csv.unix_dialect

unix_dialect 类定义了在 UNIX 系统上生成的 CSV 文件的常用属性,即使用 '\n' 作为行终止符并引用所有字段。它使用方言名称 'unix' 注册。

在 3.2 版本中添加。

class csv.Sniffer

Sniffer 类用于推断 CSV 文件的格式。

Sniffer 类提供了两个方法

sniff(sample, delimiters=None)

分析给定的sample并返回一个反映所找到参数的 Dialect 子类。如果给定了可选的delimiters参数,则将其解释为包含可能有效的分隔符字符的字符串。

has_header(sample)

分析样本文本(假定为 CSV 格式)并返回 True 如果第一行看起来是一系列列标题。检查每一列,将考虑以下两个关键标准之一来估计样本是否包含标题

  • 第二行到第 n 行包含数值

  • 第二行到第 n 行包含字符串,其中至少有一个值的长度与该列的推定标题的长度不同。

采样第一行之后的二十行;如果超过一半的列 + 行满足条件,则返回 True

注意

此方法是一种粗略的启发式方法,可能会产生误报和漏报。

关于 Sniffer 用法的示例

with open('example.csv', newline='') as csvfile:
    dialect = csv.Sniffer().sniff(csvfile.read(1024))
    csvfile.seek(0)
    reader = csv.reader(csvfile, dialect)
    # ... process CSV file contents here ...

csv 模块定义了以下常量

csv.QUOTE_ALL

指示 writer 对象引用所有字段。

csv.QUOTE_MINIMAL

指示 writer 对象仅引用包含特殊字符(如delimiterquotecharlineterminator 中的任何字符)的字段。

csv.QUOTE_NONNUMERIC

指示 writer 对象引用所有非数值字段。

指示 reader 对象将所有未引用的字段转换为 float 类型。

csv.QUOTE_NONE

指示 writer 对象永不引用字段。当当前 delimiter 出现在输出数据中时,它前面会加上当前 escapechar 字符。如果未设置 escapechar,则如果遇到任何需要转义的字符,写入器将引发 Error

指示 reader 对象不对引号字符执行任何特殊处理。

csv.QUOTE_NOTNULL

指示 writer 对象引用所有不是 None 的字段。这与 QUOTE_ALL 类似,只是如果字段值为 None,则会写入一个空(未引用)的字符串。

指示 reader 对象将空(未引用)的字段解释为 None,否则其行为与 QUOTE_ALL 相同。

在 3.12 版本中添加。

csv.QUOTE_STRINGS

指示 writer 对象始终在字符串字段周围放置引号。这与 QUOTE_NONNUMERIC 类似,只是如果字段值为 None,则会写入一个空(未引用)的字符串。

指示 reader 对象将空(未引用)的字符串解释为 None,否则其行为与 QUOTE_NONNUMERIC 相同。

在 3.12 版本中添加。

csv 模块定义了以下异常

exception csv.Error

当检测到错误时,任何函数都会引发此异常。

方言和格式化参数

为了更容易指定输入和输出记录的格式,特定的格式化参数被分组到方言中。方言是 Dialect 类的子类,其中包含描述 CSV 文件格式的各种属性。创建 readerwriter 对象时,程序员可以指定一个字符串或 Dialect 类的子类作为方言参数。除了或代替 dialect 参数,程序员还可以指定单个格式化参数,这些参数的名称与下面为 Dialect 类定义的属性相同。

方言支持以下属性

Dialect.delimiter

用于分隔字段的单字符字符串。它默认为 ','

Dialect.doublequote

控制如何引用出现在字段内的 quotechar 实例。当 True 时,该字符会加倍。当 False 时,escapechar 用作 quotechar 的前缀。它默认为 True

在输出时,如果 doublequoteFalse 并且未设置 escapechar,则如果在字段中找到 quotechar,将引发 Error

Dialect.escapechar

一个单字符字符串,当quoting设置为QUOTE_NONE,且当doublequoteFalse时,由写入器用于转义delimiterquotechar。 在读取时,escapechar 会删除后续字符的任何特殊含义。 它默认为 None,表示禁用转义。

3.11 版本已更改: 不允许使用空的 escapechar

Dialect.lineterminator

用于终止 writer 生成的行的字符串。 它默认为 '\r\n'

注意

reader 被硬编码为识别 '\r''\n' 作为行尾,并忽略 lineterminator。 此行为在未来可能会更改。

Dialect.quotechar

用于引用包含特殊字符(例如 delimiterquotechar)或包含换行符的字段的单字符字符串。 它默认为 '"'

3.11 版本已更改: 不允许使用空的 quotechar

Dialect.quoting

控制写入器何时生成引号以及读取器何时识别引号。 它可以采用任何 QUOTE_* 常量,并默认为 QUOTE_MINIMAL

Dialect.skipinitialspace

当为 True 时,忽略紧跟在 delimiter 后的空格。 默认为 False

Dialect.strict

当为 True 时,在 CSV 输入错误时引发异常 Error。 默认为 False

读取器对象

读取器对象(DictReader 实例和 reader() 函数返回的对象)具有以下公共方法

csvreader.__next__()

以列表形式(如果对象是从 reader() 返回的)或字典形式(如果它是 DictReader 实例)返回读取器的可迭代对象的下一行,并根据当前的 Dialect 进行解析。 通常你应该将其作为 next(reader) 调用。

读取器对象具有以下公共属性

csvreader.dialect

一个对解析器使用的方言的只读描述。

csvreader.line_num

从源迭代器读取的行数。 这与返回的记录数不同,因为记录可以跨越多行。

DictReader 对象具有以下公共属性

DictReader.fieldnames

如果未在创建对象时作为参数传递,则此属性将在首次访问或从文件中读取第一条记录时初始化。

写入器对象

writer 对象(DictWriter 实例和 writer() 函数返回的对象)具有以下公共方法。 对于 writer 对象,row 必须是字符串或数字的可迭代对象,对于 DictWriter 对象,row 必须是将字段名称映射到字符串或数字的字典(首先通过 str() 传递它们)。 请注意,复数会用括号括起来写入。 这可能会给其他读取 CSV 文件的程序带来一些问题(假设它们完全支持复数)。

csvwriter.writerow(row)

根据当前的 Dialectrow 参数写入写入器的文件对象。 返回对底层文件对象的 write 方法的调用返回值。

3.5 版本已更改: 添加了对任意可迭代对象的支持。

csvwriter.writerows(rows)

根据当前方言将 rows 中的所有元素(一个如上所述的 row 对象的可迭代对象)写入写入器的文件对象。

写入器对象具有以下公共属性

csvwriter.dialect

一个对写入器使用的方言的只读描述。

DictWriter 对象具有以下公共方法

DictWriter.writeheader()

将包含字段名称(如构造函数中指定)的行写入写入器的文件对象,并根据当前的方言进行格式化。 返回内部使用的 csvwriter.writerow() 调用的返回值。

在 3.2 版本中添加。

3.8 版本已更改: writeheader() 现在还返回其内部使用的 csvwriter.writerow() 方法的返回值。

示例

读取 CSV 文件的最简单示例

import csv
with open('some.csv', newline='') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

读取具有替代格式的文件

import csv
with open('passwd', newline='') as f:
    reader = csv.reader(f, delimiter=':', quoting=csv.QUOTE_NONE)
    for row in reader:
        print(row)

相应的最简单的写入示例是

import csv
with open('some.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerows(someiterable)

由于 open() 用于打开 CSV 文件进行读取,该文件默认会使用系统默认编码(请参阅 locale.getencoding())解码为 unicode。 要使用不同的编码解码文件,请使用 open 的 encoding 参数

import csv
with open('some.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

这同样适用于使用系统默认编码以外的编码写入:在打开输出文件时指定 encoding 参数。

注册新的方言

import csv
csv.register_dialect('unixpwd', delimiter=':', quoting=csv.QUOTE_NONE)
with open('passwd', newline='') as f:
    reader = csv.reader(f, 'unixpwd')

稍微高级一点的读取器用法——捕获和报告错误

import csv, sys
filename = 'some.csv'
with open(filename, newline='') as f:
    reader = csv.reader(f)
    try:
        for row in reader:
            print(row)
    except csv.Error as e:
        sys.exit('file {}, line {}: {}'.format(filename, reader.line_num, e))

虽然该模块不直接支持解析字符串,但可以很容易地完成

import csv
for row in csv.reader(['one,two,three']):
    print(row)

脚注