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='excel', **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()
csv.field_size_limit(new_limit)

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

csv 模块定义了以下类

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

创建一个类似于常规 reader 的对象,但将每行中的信息映射到一个 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)

创建一个类似于常规 writer 的对象,但将字典映射到输出行。fieldnames 参数是一个键的序列,用于标识传递给 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 生成的 TAB 分隔文件的通常属性。它以方言名称 '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 对象仅引用那些包含特殊字符(如 delimiterquotechar'\r''\n'lineterminator 中的任何字符)的字段。

csv.QUOTE_NONNUMERIC

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

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

备注

某些数值类型,如 boolFractionIntEnum,其字符串表示无法转换为 float。它们无法在 QUOTE_NONNUMERICQUOTE_STRINGS 模式下读取。

csv.QUOTE_NONE

指示 writer 对象从不引用字段。当当前的 delimiterquotecharescapechar'\r''\n'lineterminator 中的任何字符出现在输出数据中时,它前面会加上当前的 escapechar 字符。如果未设置 escapechar,则在遇到任何需要转义的字符时,writer 将引发 Error。将 quotechar 设置为 None> 以防止其转义。

指示 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

writer 用于转义需要转义的字符的单字符字符串

  • 如果 quoting 设置为 QUOTE_NONE,则 delimiterquotechar'\r''\n'lineterminator 中的任何字符都会被转义;

  • 如果 doublequoteFalse,则 quotechar 会被转义;

  • escapechar 本身。

读取时,escapechar 会移除其后字符的任何特殊含义。它默认为 None,表示禁用转义。

在 3.11 版本发生变更: 不允许空的 escapechar

Dialect.lineterminator

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

备注

reader 硬编码为识别 '\r'> 或 '\n' 作为行尾,并忽略 lineterminator。此行为将来可能会改变。

Dialect.quotechar

用于引用包含特殊字符(如 delimiterquotechar)或包含换行符('\r''\n'lineterminator 中的任何字符)的字段的单字符字符串。默认为 '"'。如果 quoting 设置为 QUOTE_NONE,可以设置为 None> 以防止转义 '"'

在 3.11 版本发生变更: 不允许空的 quotechar

Dialect.quoting

控制 writer 何时生成引号以及 reader 何时识别引号。它可以是任何 QUOTE_* 常量,如果 quotechar 不是 None,则默认为 QUOTE_MINIMAL,否则默认为 QUOTE_NONE

Dialect.skipinitialspace

当为 True 时,紧跟在 delimiter 之后的空格将被忽略。默认为 False。当 delimiter=' '> 与 skipinitialspace=True> 结合使用时,不允许未加引号的空字段。

Dialect.strict

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

Reader 对象

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

csvreader.__next__()

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

Reader 对象具有以下公共属性

csvreader.dialect

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

csvreader.line_num

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

DictReader 对象具有以下公共属性

DictReader.fieldnames

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

Writer 对象

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

csvwriter.writerow(row, /)

row 参数写入 writer 的文件对象,并根据当前的 Dialect 进行格式化。返回底层文件对象的 write 方法调用的返回值。

在 3.5 版本发生变更: 增加了对任意可迭代对象的支持。

csvwriter.writerows(rows, /)

rows(一个如上所述的 row 对象的可迭代对象)中的所有元素写入 writer 的文件对象,并根据当前方言进行格式化。

Writer 对象具有以下公共属性

csvwriter.dialect

writer 使用的方言的只读描述。

DictWriter 对象具有以下公共方法

DictWriter.writeheader()

将一行带有字段名(在构造函数中指定)的数据写入 writer 的文件对象,并根据当前方言进行格式化。返回内部使用的 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')

一个更高级的 reader 用法——捕获和报告错误

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(f'file {filename}, line {reader.line_num}: {e}')

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

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

脚注