Python模块 —— CSV文件读写


前言

  把总结写到前面,参照这个框架来看条例能清晰一些。
在这里插入图片描述

一、CSV文件介绍

  CSV (Comma Separated Values) 格式是电子表格和数据库中最常见的输入、输出文件格式。在 RFC 4180 规范推出的很多年前,CSV 格式就已经被开始使用了,由于当时并没有合理的标准,不同应用程序读写的数据会存在细微的差别。由于这些细微差别就会导致我们在CSV模块中有很多参数需要独立修改。
  针对上述的差别,在Python的CSV库中有一些定义:

  • class csv.excel:用于读写excel软件生成的csv文件
  • class csv.excel_tab:用于读写特殊excel设置生成的csv文件
  • class csv.unix_dialect:用于读写unix系统生成的csv文件

  这些类都是继承自class csv.Dialect。具体我们可以参考章节三

二、操作CSV文件

2.1 打开文件CSV文件,为读写做准备

  CSV文件一般用excel和notepad都可以打开。在进行读写操作之前我们需要先打开它。我们使用open函数来打开文件,举例如下:

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

  后续我们会对打开的文件对象f进行操作。这里需要强调一点的是newline=''必须这么写。因为csv模块会操作换行符(Dialect类中的lineterminator属性),如果这里不统一,会导致无法得到我们想要的结果。

可选择性看此部分,以下是对newline参数的解释
中文译文:
  newline 控制 universal newlines 模式如何生效(它仅适用于文本模式)。它可以是 None,‘’,‘\n’,‘\r’ 和 ‘\r\n’。它的工作原理:

  • 从流中读取输入时,如果 newline 为 None,则启用通用换行模式。输入中的行可以以 ‘\n’,‘\r’ 或 ‘\r\n’ 结尾,这些行被翻译成 ‘\n’ 在返回呼叫者之前。如果它是 ‘’,则启用通用换行模式,但行结尾将返回给调用者未翻译。如果它具有任何其他合法值,则输入行仅由给定字符串终止,并且行结尾将返回给未调用的调用者。
  • 将输出写入流时,如果 newline 为 None,则写入的任何 ‘\n’ 字符都将转换为系统默认行分隔符 os.linesep。如果 newline 是 ‘’ 或 ‘\n’,则不进行翻译。如果 newline 是任何其他合法值,则写入的任何 ‘\n’ 字符将被转换为给定的字符串。

英文原文:
  newline controls how universal newlines mode works (it only applies to text mode). It can be None, ‘’, ‘\n’, ‘\r’, and ‘\r\n’. It works as follows:

  • When reading input from the stream, if newline is None, universal newlines mode is enabled. Lines in the input can end in ‘\n’, ‘\r’, or ‘\r\n’, and these are translated into ‘\n’ before being returned to the caller. If it is ‘’, universal newlines mode is enabled, but line endings are returned to the caller untranslated. If it has any of the other legal values, input lines are only terminated by the given string, and the line ending is returned to the caller untranslated.
  • When writing output to the stream, if newline is None, any ‘\n’ characters written are translated to the system default line separator, os.linesep. If newline is ‘’ or ‘\n’, no translation takes place. If newline is any of the other legal values, any ‘\n’ characters written are translated to the given string.

2.2 普通读写: csv.read()与csv.write()

2.2.1 csv.reader(csvfile, dialect=‘excel’, **fmtparams)

  reader()是csv模块内的一个函数。当使用open()打开csv文件并得到文件对象后,可以把这个文件对象传入reader()函数。
  CSV原始数据:

Spam,Spam,Spam,Spam,Spam,Baked Beans
Spam,Lovely Spam,Wonderful Spam

  代码:

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

在这里插入图片描述
  reader()会返回一个reader对象,这是一个可迭代对象,该对象里面的每一个元素都是一个列表,每一个列表都对应了csv文件中的一行。

2.2.2 csv.writer(csvfile, dialect=‘excel’, **fmtparams)

  writer()是csv模块内的一个函数。当使用open()打开csv文件并得到文件对象后,可以把这个文件对象传入writer()函数。writer()函数会返回一个writer对象,可以调用该对象的方法将字符串文本写入csv文件。而writer对象有两个方法可用于写入数据:

  • writerow(row): 将 row 形参写入到 writer 的文件对象,一个列表写入一行。也就是一个可迭代对象的每个元素为这一行的一个数据。每个数据默认逗号分割。
  • writerows(rows): 将 rows*(即能迭代出多个上述 *row 对象的迭代器)中的所有元素写入 writer 的文件对象,一个列表的每一个元素写入一行。也就是一个可迭代对象的每个元素再进行迭代写入一行。相当于他写入了一个二维数组,数组的第一个维度是一行,第二个维度是一个个数据单元。

  不是很好理解,我们举例说明:

import csv
with open('eggs.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['line0'] + ['lin1'])
    writer.writerow(['line2', 'line3', 'line4'])
    writer.writerows(['line0'] + ['lin1'])
    writer.writerows(['line2', 'line3', 'line4'])

在这里插入图片描述

2.3 使用csv.DictReader()与csv.DictWriter()读写

  这两个与上面的的reader()和writer()不太相同,csv.DictReader()与csv.DictWriter()是类。csv文件中存在映射关系(即csv文件的第一行为表格中的表头)的时候我们用此类处理数据就很方便,当然仅仅值方便而已不是必须用此方法,也是可以用上一章节的read()和write()处理数据的。

xy
a1
b2
c3

  上表中我们认为x, y那一行为表头,其余行为数据。

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

  创建一个对象,该对象在操作上类似于常规 reader,但是将每行中的信息映射到一个 dict,该 dict 的键由 fieldnames 可选参数给出。
  fieldnames 参数是一个 sequence。如果省略 fieldnames,则文件 f 第一行中的值将用作字段名。无论字段名是如何确定的,字典都将保留其原始顺序。
  下面我们用代码举个例子:

import csv
import collections
with open('example_table.csv', 'r', newline='') as csvfile:
    dict_reader = csv.DictReader(csvfile)
    for row in dict_reader: # type: collections.OrderedDict
        element_1 = row['x']
        element_2 = row['y']
        for key, val in row.items():
            print(key, val)
        print(row)

在这里插入图片描述
  从上面的例子我们看到fieldnames是被省略的,我们无法通过字典方式取到第一行数据而只能使用dict_reader.fieldnames来取第一行数据。因为他默认第一行为fieldnames字段。如果我们主动写入一个fieldnames,不取第一行为fieldnames,那么就可以通过字典方式检索第一行数据了。
在这里插入图片描述
  如果某一行中的字段多于字段名,则剩余数据会被放入一个列表,并与 restkey 所指定的字段名 (默认为 None) 一起保存。 如果某个非空白行的字段少于字段名,则缺失的值会使用 restval 的值来填充 (默认为 None)。
  下面我们用代码举个例子,下表中第二行y列没有数据,第三列没有表头并多出一个数据@:

xy
a1
b
c3@
import csv
import collections
with open('example_table.csv', 'r', newline='') as csvfile:
    dict_reader = csv.DictReader(csvfile, restkey='z', restval='default_value')
    for row in dict_reader: # type: collections.OrderedDict
        print(row)

在这里插入图片描述

2.3.2 class csv.DictWriter(f, fieldnames, restval=‘’, extrasaction=‘raise’, dialect=‘excel’, *args, **kwds)

  DictWriter的操作类似于常规的writer(),但DictWriter会将字典写入(映射)到csv文件的行中。即通过DictWriter写入的行,都必须是字典形式,这点和writer()还是有区别的。

DictWriter与DictReader是一对,互为反向操作。所以DictWriter也是靠fieldnames为Key操作的。需要注意:

  • 因为是写入,所以实例DictWriter时必须要有fieldnames字段(因为他不是读文件不知道表头是什么,所以必须要给一个key值)
  • 写入CSV文件时首先要写入表头(fieldnames字段),使用DictWriter.writeheader()
  • writerow(row)中的row必须是字典形式

  举个例子:

import csv

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

    writer.writeheader()  # 要先写入表头,写入顺序与fieldnames列表顺序相同
    writer.writerow({'first_name': 'Baked', 'last_name': 'Beans'})
    writer.writerow({'first_name': 'Lovely', 'last_name': 'Spam'})
    writer.writerow({'first_name': 'Wonderful', 'last_name': 'Spam'})

  结果如下:
在这里插入图片描述

三、csv.Dialect类定义CSV格式的细微差别

  Dialect 类是一个容器类,其属性包含有如何处理双引号、空白符、分隔符等的信息。也就是说各个CSV的变种格式都是其中一种或多种属性有所改变。其属性举例如下:

  • Dialect.delimiter: 一个用于分隔字段的单字符,默认为 ‘,’。
  • Dialect.doublequote: 控制出现在字段中的 引号字符 本身应如何被引出。当该属性为 True 时,双写引号字符。如果该属性为 False,则在 引号字符 的前面放置 转义符。默认值为 True。
  • Dialect.escapechar
  • Dialect.lineterminator: 放在 writer 产生的行的结尾,默认为 ‘\r\n’。
  • Dialect.quotechar: 一个单字符,用于包住含有特殊字符的字段,特殊字符如 定界符 或 引号字符 或换行符。默认为 ‘"’。
  • Dialect.quoting: 控制 writer 何时生成引号以及 reader 何时识别引号。 它可以设为任意 QUOTE_* 常量 并且默认为 QUOTE_MINIMAL。下面是他的可选项:
    • csv.QUOTE_ALL:指示 writer 对象给所有字段加上引号。
    • csv.QUOTE_MINIMAL:指示 writer 对象仅为包含特殊字符(例如 定界符、引号字符 或 行结束符 中的任何字符)的字段加上引号。
    • csv.QUOTE_NONNUMERIC:指示 writer 对象为所有非数字字段加上引号。指示 reader 将所有未加引号的字段转换为 float 类型。
    • csv.QUOTE_NONE:指示 writer 对象不使用引号引出字段。
    • csv.QUOTE_NOTNULL
    • csv.QUOTE_STRINGS
  • Dialect.skipinitialspace: 当为 True 时,紧接在 delimiter 之后空格会被忽略。 默认值为 False。
  • Dialect.strict: 如果为 True,则在输入错误的 CSV 时抛出 Error 异常。默认值为 False。

  举个例子:

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.write()中去定义各项参数一两次还好,如果每次写入时都要赋值这么多参数我们会发现很累,我们可以选在建立一个csv.Dialect类的子类,将他告诉解释器。这样我们直接调用这个子类相当于应用了上述所有参数。不过我们不是直接建立类而是使用了一个方法。

import csv
# 注册一个CSV格式变种,并命名为'new_dialect'
csv.register_dialect('new_dialect', delimiter=' ', quotechar='|', quoting=csv.QUOTE_MINIMAL)
with open('eggs.csv', 'w', newline='') as csvfile:
    spamwriter = csv.writer(csvfile, dialect= 'new_dialect')  # 这里设定参数dialect= 'new_dialect'
    spamwriter.writerow(['Spam'] * 5 + ['Baked Beans'])
    spamwriter.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])

  所有可用的 Dialect 名称会由 list_dialects() 返回:

import csv
dialects_list = csv.list_dialects()  # dialects_list: ['excel', 'excel-tab', 'unix']

  从上面例子可以看出结果符合章节一中所说的CSV库已经定义的三个类csv.excel、csv.excel_tab和csv.unix_dialect。我们把这行加到上面代码中去:

import csv
dialects_list_default = csv.list_dialects()  # dialects_list: ['excel', 'excel-tab', 'unix']
csv.register_dialect('new_dialect', delimiter=' ', quotechar='|', quoting=csv.QUOTE_MINIMAL)
with open('eggs.csv', 'w', newline='') as csvfile:
    spamwriter = csv.writer(csvfile, dialect= 'new_dialect')
    spamwriter.writerow(['Spam'] * 5 + ['Baked Beans'])
    spamwriter.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])
dialects_list_new = csv.list_dialects()  # dialects_list: ['excel', 'excel-tab', 'unix', 'new_dialect']

  从上面代码中我们可以看到差异,dialects_list_new比dialects_list_default多了一个’new_dialect’是我们新加的。

注意: 所有csv.register_dialect()添加的dialect都继承自’excel’,未做修改的属性值都默认为’excel’对应的属性值。
这一段是Python csv库中的代码
在这里插入图片描述

四、总结

普通读写
Dict读写
将文件对象csvfile传入csv.reader()
,获取可迭代的reader对象
reader=csv.reader(csvfile)
对reader使用for语句取出每一行数据
将文件对象csvfile传入csv.writer()
,获取writer对象
writer=csv.writer(csvfile)
writerow(row)
将 row 形参写入到 writer 的文件对象,一个列表写入一行。
writerows(*row)
将 rows*(即能迭代出多个上述 *row 对象的迭代器)
中的所有元素写入 writer 的文件对象,
一个列表的每一个元素写入一行。
将文件对象csvfile传入csv.Dictreader(),
并实例化创建一个dict_reader对象
对dict_reader使用for语句取出每一行数据,
每个数据都是一个OrderDict
将文件对象csvfile传入csv.Dictwriter(),
并实例化创建一个dict_writer对象
实例化必须设置参数fieldnames
writerow(row)
row必须为字典
dict_reader.fieldnames获取表头
先使用dict_writer.header()
,写入表头filednames
CSV模块
通过open()打开文件返回文件对象csvfile
需要newline=''
csv.Dialect定义CSV变种

参考文章

  1. CSV 文件读写
  2. python文件处理-CSV文件的读取、处理、写入
  • 38
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值