13.1. csvCSV 文件的读写

新版本 2.3。

所谓的 CSV(Comma Separated Values)格式是电子表格和数据库导入和导出最常见的格式。实际上并不存在"CSV 标准",因此CSV文件的格式在操作中往往由许多读写它的应用程序定义。缺乏一个通用的CSV标准意味着:不同的应用程序生成和使用的数据通常会有些细微的差异。这些差异会使得处理出自多种来源的 CSV 文件让人相当恼火。尽管分隔符和引用字符不同,但是整体格式也足够相似,可以编写一个可以有效地操纵这些数据的单个模块,从编程器中隐藏读取和写入数据的细节。

csv模块实现类来读取和写入 CSV 格式的表格数据。它允许程序员“以Excel 首选的格式写入此数据"或"从Excel 生成的文件中读取数据",而不用知道 Excel 使用的 CSV 格式的具体细节。程序员也可以描述由其他应用程序理解的 CSV 格式或定义它们自己特殊用途的 CSV 格式。

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

此版本的 csv 模块不支持 Unicode 的输入。此外,目前有一些关于 ASCII NUL 字符的问题。因此,为了安全起见,所有输入应该都是UTF-8 或可打印的 ASCII;请参见示例一节中的示例。

请参见

PEP 305 - CSV File API
The Python Enhancement Proposal which proposed this addition to Python.

13.1.1. 模块的内容

csv 模块定义了以下函数:

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

返回一个reader 对象,它将遍历csvfile中的每一行。csvfile可以是任何支持迭代器协议的对象,每次调用它的next()方法时返回一个字符串 —— 文件对象和列表对象都可以。如果csvfile是一个文件对象,在'b'标志对于打开文件有所有不同的平台上,它必须带有该标志。dialect 是一个可选参数,用于定义一个特定CSV 程序的参数集。它可以是Dialect类的子类的一个实例,或者list_dialects()函数返回的字符串中的一个。可以指定另外一个可选的关键字参数fmtparams来覆盖当前dialect中的格式设置参数。关于dialect和格式设置参数的全部细节,请参阅Dialects and Formatting Parameters一节。

从 csv 文件中读取的每一行作为一个字符串列表返回。不执行任何自动数据类型转换。

一个简短的用法示例:

>>> import csv
>>> with open('eggs.csv', 'rb') 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

2.5 版本中的更改:解析器对于多行引用的字段现在变得更加严格。以前,如果一行结束在无终止的换行字符的引用字段内,换行符将插入到的返回的字段。读取文件,其中载有马车返回字段内的字符时,这种行为造成问题。行为更改为返回的字段,而不插入换行符。因此,如果换行符嵌入领域很重要的输入应分成行保留换行符字符的方式。

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

返回一个writer对象,负责将用户的数据转换为分隔字符串并写入给定的类文件对象中。csvfile可以是任何带有write()方法的对象。如果csvfile是一个文件对象,在'b'标志对于打开文件有所有不同的平台上,它必须带有该标志。dialect 是一个可选参数,用于定义一个特定CSV 程序的参数集。它可以是Dialect类的子类的一个实例,或者list_dialects()函数返回的字符串中的一个。可以指定另外一个可选的关键字参数fmtparams来覆盖当前dialect中的格式设置参数。关于dialect和格式设置参数的全部细节,请参阅Dialects and Formatting Parameters一节。为了使它尽可能容易地与实现 DB API 的模块通信,None 值被作为空字符串写入。虽然这不是一个可逆转变,这使得它更容易转储SQL NULL 数据值到CSV 文件中,而不用先处理一下cursor.fetch*调用返回的数据。所有其它非字符串数据在写入之前利用str()进行字符串化。

一个简短的用法示例:

import csv
with open('eggs.csv', 'wb') 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 must be a string or Unicode object.dialect 可以通过传递Dialect的一个子类或者fmtparams关键字参数指定,或者两者同时指定,此时关键字参数将覆盖dialect 的参数。关于dialect和格式设置参数的全部细节,请参阅Dialects and Formatting Parameters一节。

csv.unregister_dialect(name)

删除 dialect 的注册表中与name相关联的dialect。如果name不是注册过的dialect名称,将引发一个Error

csv.get_dialect(name)

返回与name相关联的dialect。如果name不是注册过的dialect名称,将引发一个Error

2.5 版本中的更改:现在,此函数返回一个不可变的Dialect以前返回请求的dialect 的一个实例。用户可以修改基础类,借此改变活动的reader 和writer 的行为。

csv.list_dialects()

返回所有已注册的dialect 的名称。

csv.field_size_limit([new_limit])

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

版本 2.5 中新增。

csv 模块定义了以下类:

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

创建一个对象,它的操作类似一个普通的reader,但将读取的信息映射成一个字典,字典的键通过可选的参数fieldnames给出。fieldnames参数是一个序列,其元素与输入数据的字段与按顺序相关联。这些元素成为产生的字典的键。如果省略fieldnames参数,csvfile的第一行的值将用作fieldnames。如果读取的行字段数要多于字段名序列,剩下的数据将添加成一个序列,通过restkey的值索引。如果读取的行的字段数少于fieldnames序列,剩余的键将作为可选的restval参数的值。任何其它可选或关键字参数将传递给底层的reader实例。

class csv.DictWriter(csvfile, fieldnames, restval='', extrasaction='raise', dialect='excel', *args, **kwds)

创建一个对象,它的操作类似一个普通的writer,但将字典映射至行输出(output rows)。字段名参数是标识的顺序传递给writerow()方法的字典中的值写入csvfile键的序列可选的restval参数指定要写入如果字典缺少中字段名的键的值。如果传递给writerow()方法的字典包含字段名中找不到钥匙,可选extrasaction参数指示采取什么行动。如果设置为'raise',则会引发ValueError如果设置为'ignore',则会忽略字典中的额外值。任何其他可选或关键字参数都会传递给底层的writer实例。

请注意,与DictReader类不同, DictWriter字段名参数不是可选的。由于 Python 的dict对象不排序的不是足够的信息,可用来推断应将行写入csvfile的顺序。

class csv.Dialect

Dialect类是一个主要用来存储其属性的容器类,用于定义一个特定的readerwriter实例的参数。

class csv.excel

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

class csv.excel_tab

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

class csv.Sniffer

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

Sniffer类提供两种方法:

sniff(sample, delimiters=None)

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

has_header(sample)

分析示例文本(假定为 CSV 格式) ,如果第一行看上去像一个系列列的标题则返回True

一个使用Sniffer的例子:

with open('example.csv', 'rb') 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只引用包含特殊字符的字段,例如分隔符引用字符行终结符中的任何一个字符。

csv.QUOTE_NONNUMERIC

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

指示reader 转换所有的非引用字段为float类型。

csv.QUOTE_NONE

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

指示reader 不用对引号字符做特殊处理。

csv 模块定义了以下异常:

exception csv.Error

任何函数中检测到错误时抛出。

13.1.2. Dialects 和 格式参数

为了便于指定输入和输出记录的格式,具体格式参数被组合成方言。方言是有一套具体的方法和一种单一validate ()方法的方言类的子类。创建读取器编写器对象时,程序员可以作为方言参数指定一个字符串或方言类的子类。以增补或代替方言参数,程序员也可以指定单个格式的参数,具有相同的定义下面方言类的属性的名称。

Dialects 支持以下属性:

Dialect.delimiter

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

Dialect.doublequote

控制如何quotechar字段内出现的实例应该是被自己被引述。True,人物是翻了一番。Falseescapechar是作为quotechar的前缀的使用。其默认值为True

对输出,如果doublequoteFalse ,并且设置没有escapechar ,则引发错误如果quotechar在一个字段中找到。

Dialect.escapechar

这位作家用于转义分隔符,如果引用设置为QUOTE_NONEquotechar doublequoteFalse时的单字符字符串。在读时, escapechar从以下字符中移除任何特殊的意义。它将默认为,这将禁用转义。

Dialect.lineterminator

用来终止作家所生产出来的行的字符串。它将默认为''.

读者是硬编码来识别或''或' '作为结束的行,并忽略lineterminator这种行为可能在将来会改变。

Dialect.quotechar

A one-character string used to quote fields containing special characters, such as the delimiter or quotechar, or which contain new-line characters. It defaults to '"'.

Dialect.quoting

当报价应由作者生成,并由读者认可的控件。它可以在任何QUOTE_ * (参见部分模块内容) 的常量和默认值为QUOTE_MINIMAL

Dialect.skipinitialspace

当值为True时,后接分隔符的空白将被忽略。默认值为False

Dialect.strict

当值为True时,对错误的CSV输入,抛出Error异常。默认值为False

13.1.3. Reader 对象

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

csvreader.next()

以列表形式返回该reader 的可迭代对象的下一行,依据当前的dialect 解析。

Reader 对象具有下列公共属性:

csvreader.dialect

解析器正在使用的dialect 的一个只读描述。

csvreader.line_num

从源迭代器读取的行数。不,这是作为记录可以跨多个行,返回的记录的数目相同。

新版本 2.5 中的。

DictReader 对象具有下列公共属性:

csvreader.fieldnames

如果不作为参数传递,创建对象时,此属性被初始化后第一次访问或当从文件中读取第一个记录。

在 2.6 版本中更改。

13.1.4. Writer 对象

Writer对象(DictWriter的实例和writer()函数返回的对象)有下列公开方法。row对于Writer对象必须是一个字符串或数字序列,对于DictWriter对象必须是一个字典,映射字段名到字符串或数字(首先将它们传递给str())。请注意,复数写出时由括号包围。这可能会导致其它读取CSV 文件的程序出现一些问题 (假设它们完全支持复数)。

csvwriter.writerow(row)

row 参数写入writer 的文件对象,格式依据当前的dialect。

csvwriter.writerows(rows)

写入所有的rows参数(一个上文所述的row对象的列表)到writer 的文件对象,格式依据当前的dialect。

Writer 对象具有下列公共属性:

csvwriter.dialect

Writer 正在使用的dialect 的一个只读描述。

DictWriter 对象具有下面的公共方法:

DictWriter.writeheader()

写一个字段名称行(在构造函数中指定)。

在 2.7 版本中新增。

13.1.5. 示例

读一个 CSV 文件的最简单的例子:

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

读具有另一种格式的文件:

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

相应的简单可能写作示例是:

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

注册一种新方言:

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

读者一个稍微更先进的用途 — — 捕捉并报告错误:

import csv, sys
filename = 'some.csv'
with open(filename, 'rb') as f:
    reader = csv.reader(f)
    try:
        for row in reader:
            print row
    except csv.Error as e:
        sys.exit('file %s, line %d: %s' % (filename, reader.line_num, e))

,而该模块不能直接支持解析字符串,它可以很容易做到:

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

Csv模块不直接支持读取和写入 unicode),但它是 8-位-清洁保存为 ASCII NUL 字符的一些问题。所以你可以写函数或类中处理的编码和解码为你,只要你避免像 UTF 16 使用 NULs 的编码。UTF-8 被建议。

下面的unicode_csv_reader()是一种生成器打包csv.reader处理 Unicode CSV 数据 (Unicode 字符串的列表)。 utf_8_encoder()是一种发电机,将 Unicode 字符串编码为 utf-8,一个字符串 (或行) 一次。CSV 读者分析编码的字符串和unicode_csv_reader()解码 UTF 8 编码细胞回 Unicode:

import csv

def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
    # csv.py doesn't do Unicode; encode temporarily as UTF-8:
    csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),
                            dialect=dialect, **kwargs)
    for row in csv_reader:
        # decode UTF-8 back to Unicode, cell by cell:
        yield [unicode(cell, 'utf-8') for cell in row]

def utf_8_encoder(unicode_csv_data):
    for line in unicode_csv_data:
        yield line.encode('utf-8')

对于所有其他编码可以使用下面的UnicodeReaderUnicodeWriter类。他们采取额外的编码参数在其构造函数中,并确保数据会通过真实的读者或作者为 utf-8 编码:

import csv, codecs, cStringIO

class UTF8Recoder:
    """
    Iterator that reads an encoded stream and reencodes the input to UTF-8
    """
    def __init__(self, f, encoding):
        self.reader = codecs.getreader(encoding)(f)

    def __iter__(self):
        return self

    def next(self):
        return self.reader.next().encode("utf-8")

class UnicodeReader:
    """
    A CSV reader which will iterate over lines in the CSV file "f",
    which is encoded in the given encoding.
    """

    def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
        f = UTF8Recoder(f, encoding)
        self.reader = csv.reader(f, dialect=dialect, **kwds)

    def next(self):
        row = self.reader.next()
        return [unicode(s, "utf-8") for s in row]

    def __iter__(self):
        return self

class UnicodeWriter:
    """
    A CSV writer which will write rows to CSV file "f",
    which is encoded in the given encoding.
    """

    def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
        # Redirect output to a queue
        self.queue = cStringIO.StringIO()
        self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
        self.stream = f
        self.encoder = codecs.getincrementalencoder(encoding)()

    def writerow(self, row):
        self.writer.writerow([s.encode("utf-8") for s in row])
        # Fetch UTF-8 output from the queue ...
        data = self.queue.getvalue()
        data = data.decode("utf-8")
        # ... and reencode it into the target encoding
        data = self.encoder.encode(data)
        # write to the target stream
        self.stream.write(data)
        # empty queue
        self.queue.truncate(0)

    def writerows(self, rows):
        for row in rows:
            self.writerow(row)