# 14.1. `csv` - CSV文件读取和写入

源代码： Lib / csv.py

所谓的CSV（Comma Separated Values）格式是电子表格和数据库最常见的导入和导出格式。在尝试以 RFC 4180以标准化方式描述格式之前，CSV格式已使用多年。The lack of a well-defined standard means that subtle differences often exist in the data produced and consumed by different applications.这些差异可能会让处理来自多个来源的CSV文件烦人。尽管分隔符和引用字符不同，但是整体格式是足够相似的，以致于可以编写能够有效地操作这样的数据的单个模块，隐藏从编程器读取和写入数据的细节。

`csv`模块实现以CSV格式读取和写入表格数据的类。它允许程序员说，“以Excel首选的格式写入数据”或“从Excel生成的此文件中读取数据”，而不知道Excel使用的CSV格式的确切详细信息。程序员还可以描述其他应用程序理解的CSV格式，或定义自己的特殊用途CSV格式。

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

也可以看看

PEP 305 - CSV文件API
  Python 改善建议书 提出了对Python的添加。

## 14.1.1. 模块内容

`csv`模块定义以下函数：

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

  返回一个读取器对象，它将在给定的csvfile中迭代。csvfile可以是任何支持iterator协议的对象，并且每次调用`__next__()`方法时返回一个字符串 - file objects和列表对象都是合适的。如果csvfile是文件对象，则应使用`newline=''`打开它。[1]可以给出一个可选的方言参数，用于定义特定于某个CSV方言的一组参数。它可以是`Dialect`类的子类的实例或`list_dialects()`函数返回的字符串之一。可以给出其他可选的fmtparams关键字参数以覆盖当前方言（方言译为”编码分格“更好）中的各个格式化参数。有关方言和格式化参数的完整详细信息，请参见Dialects and Formatting Parameters一节。

  从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]打开。可以给出可选的方言参数，用于定义特定于特定CSV方言的一组参数。它可以是`Dialect`类的子类的实例或`list_dialects()`函数返回的字符串之一。可以给出其他可选的fmtparams关键字参数以覆盖当前方言中的各个格式化参数。有关方言和格式化参数的完整详细信息，请参见Dialects and Formatting Parameters一节。为了尽可能容易地与实现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]])

  将方言与名称相关联。名称必须是字符串。方言可以通过传递`Dialect`的子类或fmtparams关键字参数或两者来指定，其中关键字参数覆盖方言的参数。有关方言和格式化参数的完整详细信息，请参见Dialects and Formatting Parameters一节。

`csv.``unregister_dialect`(name)

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

`csv.``get_dialect`(name)

  返回与名称关联的方言。如果名称不是注册的方言名称，则会引发`Error`。此函数返回不可变的`Dialect`。

`csv.``list_dialects`()

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

`csv.``field_size_limit`([new_limit])

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

`csv`模块定义以下类：

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

  创建一个对象，其操作类似于普通读取器，但将读取的信息映射到一个dict中，其中的键由可选的fieldnames参数给出。fieldnames参数是一个`sequence`，其元素按顺序与输入数据的字段相关联。这些元素成为结果字典的键。如果省略fieldnames参数，则csvfile的第一行中的值将用作字段名称。如果读取的行具有比字段名序列更多的字段，则剩余数据将作为键值为restkey的序列添加。如果读取的行具有比字段名序列少的字段，则剩余的键使用可选的restval参数的值。任何其他可选或关键字参数都传递给底层的`reader`实例。

  简短用法示例：

      >>> import csv
      >>> with open('names.csv') as csvfile:
      ...     reader = csv.DictReader(csvfile)
      ...     for row in reader:
      ...         print(row['first_name'], row['last_name'])
      ...
      Baked Beans
      Lovely Spam
      Wonderful Spam

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

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

  请注意，与`DictReader`类不同，`DictWriter`的fieldnames参数不是可选的。由于Python的`dict`对象没有排序，因此没有足够的信息来推断将该行写入到csvfile的顺序。

  简短用法示例：

      import csv

      with open('names.csv', 'w') 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`类是主要依赖于其属性的容器类，用于定义特定`reader`或`writer`实例的参数。

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)

    分析给定的示例并返回一个反映所找到的参数的`Dialect`子类。如果给出了可选的分隔符参数，它将被解释为包含可能的有效分隔符字符的字符串。

  `has_header`(sample)

    分析示例文本（假定为CSV格式），如果第一行显示为一系列列标题，则返回`True`。

`Sniffer`的示例使用：

    with open('example.csv') 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`对象仅引用包含特殊字符（如分隔符，quotechar或lineterminator t5>。

`csv.``QUOTE_NONNUMERIC`

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

  指示读者将所有未引用的字段转换为float。

`csv.``QUOTE_NONE`

  指示`writer`对象从不引用字段。当输出数据中出现当前定界符时，其前面为当前escapechar字符。如果未设置escapechar，如果遇到需要转义的字符，则写入程序将引发`Error`。

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

`csv`模块定义了以下异常：

exception `csv.``Error`

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

## 14.1.2. 方言和格式参数

为了更容易指定输入和输出记录的格式，特定的格式化参数被分组到方言中。方言是具有一组特定方法和单个`validate()`方法的`Dialect`类的子类。当创建`reader`或`writer`对象时，程序员可以指定`Dialect`类的字符串或子类作为方言参数。除了dialect参数外，程序员还可以指定单独的格式参数，它们与`Dialect`类的下面定义的属性具有相同的名称。

方言支持以下属性：

`Dialect.``delimiter`

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

`Dialect.``doublequote`

  控制在字段中出现的quotechar实例本身应如何引用。当`True`时，字符加倍。当`False`时，escapechar用作quotechar的前缀。默认为`True`。

  On output, if doublequote is `False` and no escapechar is set, `Error` is raised if a quotechar is found in a field.

`Dialect.``escapechar`

  写入器将分隔符（如果引用）转义的一个字符串设置为`QUOTE_NONE`和quotechar如果doublequote是`False`。读取时，escapechar会删除以下字符中的任何特殊含义。它默认为`None`，它禁用转义。

`Dialect.``lineterminator`

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

  注意

  `reader`是硬编码的，以识别`'\r'`或`'\n'`作为行尾，并忽略 lineterminator。此行为可能会在将来更改。

`Dialect.``quotechar`

  用于引用包含特殊字符（例如分隔符或quotechar）或包含换行字符的字段的单字符字符串。它默认为`'"'`。

`Dialect.``quoting`

  控制当quote应由writer生成并由reader识别。它可以接受任何`QUOTE_*`常量（参见Module Contents一节），默认为`QUOTE_MINIMAL`。

`Dialect.``skipinitialspace`

  当`True`时，紧跟分隔符后的空格将被忽略。默认值为`False`。

`Dialect.``strict`

  当`True`时，在错误的CSV输入上引发异常`Error`。默认值为`False`。

## 14.1.3. Reader 对象

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

`csvreader.``__next__`()

  返回Reader的可迭代对象的下一行作为列表，根据当前方言解析。通常你应该把它叫做`next(reader)`。

Reader对象具有以下公共属性：

`csvreader.``dialect`

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

`csvreader.``line_num`

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

DictReader对象具有以下公共属性：

`csvreader.``fieldnames`

## 14.1.4. Writer 对象

`Writer`对象（`DictWriter`实例和由`writer()`函数返回的对象）具有以下公共方法。A row must be an iterable of strings or numbers for `Writer` objects and a dictionary mapping fieldnames to strings or numbers (by passing them through `str()` first) for `DictWriter` objects. 注意，复数用柔义包围。这可能会导致一些问题，其他程序读取CSV文件（假设他们支持复数）。

`csvwriter.``writerow`(row)

  将参数row写入写入器的文件对象，根据当前方言格式化。

  在版本3.5中已更改：添加了对任意iterable的支持。

`csvwriter.``writerows`(rows)

  将所有行参数（如上所述的行对象的列表）写入writer的文件对象，根据当前方言格式化。

Writer对象具有以下公共属性：

`csvwriter.``dialect`

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

DictWriter对象具有以下公共方法：

`DictWriter.``writeheader`()

  用字段名写入一行（在构造函数中指定）。

  版本3.2中的新功能。

## 14.1.5. 实例

读取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.getpreferredencoding()`） 。要使用不同的编码对文件进行解码，请使用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)

脚注

[1] | （1，2）如果未指定`newline=''`将正确解释，在使用`\r\n` linendings写入额外`\r`的平台上将被添加。指定`newline=''`应该是安全的，因为csv模块有自己的（universal）换行处理。