第108天:Python 操作 CSV

使用过 CSV 文件都知道:如果我们的电脑中装了 WPS 或 Microsoft Office 的话,.csv 文件默认是被 Excel 打开的,那么什么是 CSV 文件?CSV 文件与 Excel 文件有什么区别?如何通过 Python 来操作 CSV 文件呢?带着这些问题我们接着往下看。

1 简介

1.1 CSV

CSV 全称 Comma-Separated Values,中文叫逗号分隔值或字符分隔值,它以纯文本形式存储表格数据(数字和文本),其本质就是一个字符序列,可以由任意数目的记录组成,记录之间以某种换行符分隔,每条记录由字段组成,通常所有记录具有完全相同的字段序列,字段间常用逗号或制表符进行分隔。CSV 文件格式简单、通用,在现实中有着广泛的应用,其中使用最多的是在程序之间转移表格数据。

1.2 CSV 与 Excel

因为 CSV 文件与 Excel 文件默认都是用 Excel 工具打开,那他们有什么区别呢?我们通过下表简单了解一下。

CSV Excel
文件后缀为 .csv 文件后缀为 .xls.xlsx
纯文本文件 二进制文件
存储数据不包含格式、公式等 不仅可以存储数据,还可以对数据进行操作
可以通过 Excel 工具打开,也可以通过文本编辑器打开 只能通过 Excel 工具打开
只能编写一次列标题 每一行中的每一列都有一个开始标记和结束标记
导入数据时消耗内存较少 导入数据时消耗内存较多

2 基本使用

Python 通过 csv 模块来实现 CSV 格式文件中数据的读写,该模块提供了兼容 Excel 方式输出、读取数据文件的功能,这样我们无需知道 Excel 所采用 CSV 格式的细节,同样的它还可以定义其他应用程序可用的或特定需求的 CSV 格式。

csv 模块中使用 reader 类和 writer 类读写序列化的数据,使用 DictReader 类和 DictWriter 类以字典的形式读写数据,下面来详细看一下相应功能。首先来看一下 csv 模块常量信息,如下所示:

属性 说明
QUOTE_ALL 指示 writer 对象给所有字段加上引号
QUOTE_MINIMAL 指示 writer 对象仅为包含特殊字符(如:定界符、引号字符、行结束符等)的字段加上引号
QUOTE_NONNUMERIC 指示 writer 对象为所有非数字字段加上引号
QUOTE_NONE 指示 writer 对象不使用引号引出字段

writer(csvfile, dialect=’excel’, **fmtparams)

返回一个 writer 对象,该对象负责将用户的数据在给定的文件类对象上转换为带分隔符的字符串。csvfile 可以是具有 write() 方法的任何对象,如果 csvfile 是文件对象,则使用 newline=’’ 打开;可选参数 dialect 是用于不同的 CSV 变种的特定参数组;可选关键字参数 fmtparams 可以覆写当前变种格式中的单个格式设置。看下示例:

1
2
3
4
5
6
7
8
9
import csv

with open('test.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['id', 'name', 'age'])
    writer.writerow(['1001', '张三', '222'])
    # 写入多行
    # data = [('1001', '张三', '21'), ('1002', '李四', '31')]
    # writer.writerows(data)

我们打开文件看一下结果,如图所示:

reader(csvfile, dialect=’excel’, **fmtparams)

返回一个 reader 对象,该对象将逐行遍历 csvfile,csvfile 可以是文件对象和列表对象,如果是文件对象要使用 newline=’’ 打开。看下示例:

1
2
3
4
5
6
7
8
>>> import csv

>>> with open('test.csv', newline='') as csvfile:
>>>     reader = csv.reader(csvfile, delimiter=' ')
>>>     for row in reader:
>>>         print(', '.join(row))
id,name,age
1001,张三,222

register_dialect(name[, dialect[, **fmtparams]])

将 name 与 dialect 关联起来。name 必须是字符串,要指定 dialect,可以给出 Dialect 的子类或给出 fmtparams 关键字参数,也可以两者都给出(此时关键字参数会覆盖 dialect 参数)。先来看一下 dialect 和 fmtparams 详细信息,如下所示:

属性 说明
delimiter 用于分隔字段的单字符,默认为逗号
doublequote 控制出现在字段中的引号字符本身应如何被引出,值为 True,双写引号字符,值为 False,则在引号字符的前面放置转义符,默认值为 True
quoting 控制 writer 何时生成引号,以及 reader 何时识别引号
lineterminator 放在 writer 产生的行的结尾,默认为 ‘\r\n’
quotechar 一个单字符,用于包住含有特殊字符(如:引号字符、换行符等)的字段,默认为 ‘”’
skipinitialspace 值为 True,忽略定界符之后的空格,默认为 False
strict 值为 True,则在输入错误的 CSV 时抛出 Error 异常,默认值为 False
escapechar 用于 writer 的单字符,在 quoting 设置为 QUOTE_NONE 的情况下转义定界符,在 doublequote 设置为 False 的情况下转义引号字符,默认为 None,表示禁用转义

下面通过一个示例作进一步了解,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> import csv

>>> csv.register_dialect('mydialect', delimiter='|', quoting=csv.QUOTE_ALL)
>>> with open('test.csv', 'w', newline='') as csvfile:
>>>     writer = csv.writer(csvfile, 'mydialect')
>>>     writer.writerow(['id', 'name', 'age'])
>>>     writer.writerow(['1001', '张三', '222'])
>>> with open('test.csv', newline='') as csvfile:
>>>     reader = csv.reader(csvfile, delimiter=' ')
>>>     for row in reader:
>>>         print(', '.join(row))
id|"name"|"age"
1001|"张三"|"222"

DictWriter(f, fieldnames, restval=’’, extrasaction=’raise’, dialect=’excel’, *args, **kwds)

创建一个对象,该对象在操作上类似常规 writer,但会将字典映射到输出行,fieldnames 参数是由键组成的序列,它指定字典中值的顺序,这些值会按指定顺序传递给 writerow() 方法并写入文件;如果字典缺少 fieldnames 中的键,则可选参数 restval 用于指定要写入的值;如果传递给 writerow() 方法的字典的某些键在 fieldnames 中找不到,则可选参数 extrasaction 用于指定要执行的操作,如果将其设置为默认值 ‘raise’,则会引发 ValueError, 如果将其设置为 ‘ignore’,则字典中的其他键值将被忽略;所有其他可选或关键字参数都传递给底层的 writer 实例。看下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> import csv

>>> with open('test.csv', 'w', newline='') as csvfile:
>>>     fieldnames = ['id', 'name', 'age']
>>>     writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
>>>     writer.writeheader()
>>>     writer.writerow({'id': '1001', 'name': '张三', 'age': '21'})
>>>     writer.writerow({'id': '1002', 'name': '李四', 'age': '31'})
>>> with open('test.csv', newline='') as csvfile:
>>>     reader = csv.reader(csvfile, delimiter=' ')
>>>     for row in reader:
>>>         print(', '.join(row))
id,name,age
1001,张三,21
1002,李四,31

DictReader(f, fieldnames=None, restkey=None, restval=None, dialect=’excel’, *args, **kwds)

创建一个对象,该对象在操作上类似于常规 reader,但是将每行中的信息映射到一个 dict,该 dict 的键由 fieldnames(是一个序列)可选参数给出,如果省略 fieldnames,则文件第一行中的值将用作字段名;如果某一行中的字段多于字段名,则其余字段将放入列表中,字段名由 restkey 指定(默认为 None),如果非空白行的字段少于字段名,则缺少的值将用 None 填充。看一下示例:

1
2
3
4
5
6
7
8
>>> import csv

>>> with open('test.csv', newline='') as csvfile:
>>>     reader = csv.DictReader(csvfile)
>>>     for row in reader:
>>>         print(row['id'], row['name'], row['age'])
1001 张三 21
1002 李四 31

Sniffer 类

用于推断 CSV 文件的格式,该类提供了如下两个方法:

sniff(sample, delimiters=None)

分析给定的 sample,如果给出可选的 delimiters 参数,则该参数会被解释为字符串,该字符串包含了可能的有效定界符。

has_header(sample)

分析示例文本(假定为 CSV 格式),如果第一行很可能是一系列列标题,则返回 True。

该类及方法使用较少,了解即可,下面通过一个示例简单了解一下。

1
2
3
4
5
6
7
8
import csv

with open('test.csv', newline='') as csvfile:
     dialect = csv.Sniffer().sniff(csvfile.read(1024))
     csvfile.seek(0)
     reader = csv.reader(csvfile, dialect)
     for row in reader:
         print(row)

Reader 对象

Reader 对象指 DictReader 实例和 reader() 函数返回的对象,下面看一下其公开属性和方法。

__next__()

返回 reader 的可迭代对象的下一行,返回值可能是列表或字典。

dialect

dialect 描述,只读,供解析器使用。

line_num

源迭代器已经读取了的行数。

fieldnames

字段名称,该属性为 DictReader 对象属性。

Writer 对象

Writer 对象指 DictWriter 实例和 writer() 函数返回的对象,下面看一下其公开属性和方法。

writerow(row)

将参数 row 写入 writer 的文件对象。

writerows(rows)

将 rows*(即能迭代出多个上述 *row 对象的迭代器)中的所有元素写入 writer 的文件对象。

writeheader()

在 writer 的文件对象中,写入一行字段名称,该方法为 DictWriter 对象方法。

dialect

dialect 描述,只读,供 writer 使用。

总结

本文介绍了 CSV 及使用 Python 操作 CSV 文件,能够通过本文的学习对 CSV 有一定了解及通过 Python 实际操作 CSV 文件。

参考

https://baike.baidu.com/item/CSV/10739?fr=aladdin

https://docs.python.org/zh-cn/3.8/library/csv.html#module-csv

示例代码:https://github.com/JustDoPython/python-100-day

Python Geek Tech wechat
欢迎订阅 Python 技术,这里分享关于 Python 的一切。