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)

返回一个 读取器对象,该对象将处理来自给定 csvfile 的行。csvfile 必须是字符串的可迭代对象,每个字符串都采用读取器定义的 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)

返回一个写入器对象,该对象负责将用户的数据转换为给定类文件对象上的分隔字符串。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 对象仅引用包含特殊字符的字段,例如 *delimiter*、*quotechar* 或 *lineterminator* 中的任何字符。

csv.QUOTE_NONNUMERIC

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

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

csv.QUOTE_NONE

指示 writer 对象永远不要引用字段。当当前 *delimiter* 出现在输出数据中时,它前面是当前的 *escapechar* 字符。如果未设置 *escapechar*,则如果遇到任何需要转义的字符,则 writer 将引发 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 模块定义了以下异常

异常 csv.Error

当检测到错误时,由任何函数引发。

方言和格式参数

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

方言支持以下属性

Dialect.delimiter

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

Dialect.doublequote

控制字段中出现的 *quotechar* 实例本身应该如何用引号括起来。当为 True 时,字符会加倍。当为 False 时,*escapechar* 将用作 *quotechar* 的前缀。默认为 True

在输出时,如果 *doublequote* 为 False 并且没有设置 *escapechar*,则如果在字段中找到 *quotechar*,则会引发 Error

Dialect.escapechar

如果 *quoting* 设置为 QUOTE_NONE,则写入器使用单字符字符串转义 *delimiter*;如果 *doublequote* 为 False,则转义 *quotechar*。在读取时,*escapechar* 会删除后续字符的任何特殊含义。默认为 None,这将禁用转义。

在 3.11 版更改: 不允许使用空的 *escapechar*。

Dialect.lineterminator

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

注意

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

Dialect.quotechar

用于将包含特殊字符(例如 *delimiter* 或 *quotechar*)或包含换行符的字段括起来的单字符字符串。默认为 '"'

在 3.11 版更改: 不允许使用空的 *quotechar*。

Dialect.quoting

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

Dialect.skipinitialspace

当为 True 时,*delimiter* 后面的空格将被忽略。默认为 False

Dialect.strict

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

读取器对象

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

csvreader.__next__()

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

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

csvreader.dialect

解析器正在使用的方言的只读描述。

csvreader.line_num

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

DictReader 对象具有以下公共属性

DictReader.fieldnames

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

写入器对象

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

csvwriter.writerow(row)

row 参数写入写入器的文件对象,格式根据当前的 Dialect。返回对底层文件对象的 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 文件进行读取,因此默认情况下,文件将使用系统默认编码解码为 Unicode(请参阅 locale.getencoding())。要使用不同的编码解码文件,请使用 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)

脚注