CSV 文件中的每行代表电子表格中的一行,逗号分割了该行中的单元格。例如,来自 http://nostarch.com/automatestuff/的电子表格 example.xlsx,在一个 CSV 文件中,看起来像这样:
4/5/2015 13:34,Apples,73
4/5/2015 3:41,Cherries,85
4/6/2015 12:46,Pears,14
4/8/2015 8:59,Oranges,52
4/10/2015 2:07,Apples,152
4/10/2015 18:10,Bananas,23
4/10/2015 2:40,Strawberries,98
我将使用这个文件作为本章的交互式环境的例子。可以从 http://nostarch.com/automatestuff/下载 example.csv,或在文本编辑器中输入文本,并保存为 example.csv。CSV 文件是简单的,缺少 Excel 电子表格的许多功能。例如, CSV 文件中:
• 值没有类型,所有东西都是字符串;
• 没有字体大小或颜色的设置;
• 没有多个工作表;
• 不能指定单元格的宽度和高度;
• 不能合并单元格;
• 不能嵌入图像或图表。
CSV 的文件的优势是简单。 CSV 文件被许多种类的程序广泛地支持,可以在文本编辑器中查看(包括 IDLE 的文件编辑器),它是表示电子表格数据的直接方式。CSV 格式和它声称的完全一致:它就是一个文本文件,具有逗号分隔的值。因为 CSV 文件就是文本文件,所以你可能会尝试将它们读入一个字符串,然后用第 8 章中学到的技术处理这个字符串。例如,因为 CSV 文件中的每个单元格有逗号分割,也许你可以只是对每行文本调用 split()方法,来取得这些值。但并非CSV 文件中的每个逗号,都表示两个单元格之间的分界。 CSV 文件也有自己的转义字符,允许逗号和其他字符作为值的一部分。 split()方法不能处理这些转义字符。因为这些潜在的缺陷,所以总是应该使用 csv 模块来读写 CSV 文件。
Reader 对象
要用 csv 模块从 CSV 文件中读取数据,需要创建一个 Reader 对象。 Reader 对象让你迭代遍历 CSV 文件中的每一行。在交互式环境中输入以下代码,同时将example.csv 放在当前工作目录中:
>>> import csv
>>> exampleFile = open('example.csv')
>>> exampleReader = csv.reader(exampleFile)
>>> exampleData = list(exampleReader)
>>> exampleData
[['4/5/2015 13:34', 'Apples', '73'], ['4/5/2015 3:41', 'Cherries', '85'],
['4/6/2015 12:46', 'Pears', '14'], ['4/8/2015 8:59', 'Oranges', '52'],
['4/10/2015 2:07', 'Apples', '152'], ['4/10/2015 18:10', 'Bananas', '23'],
['4/10/2015 2:40', 'Strawberries', '98']]
csv 模块是 Python 自带的,所以不需要安装就可以导入它。要用 csv 模块读取 CSV 文件,首先用 open()函数打开它,就像打开任何其他文本文件一样。但是,不用在 open()返回的 File 对象上调用 read()或 readlines()方法,而是将它传递给 csv.reader()函数。这将返回一个 Reader 对象,供你使用。请注意,不能直接将文件名字符串传递给 csv.reader()函数。要访问 Reader 对象中的值,最直接的方法,就是将它转换成一个普通 Python 列表, 即将它传递给 list()。在这个 Reader 对象上应用 list()函数,将返回一个列表的列表。可以将它保存在变量 exampleData 中。在交互式环境中输入 exampleData,将显示列表的列表既然已经将 CSV 文件表示为列表的列表,就可以用表达式 exampleData[row][col]来访问特定行和列的值。其中, row 是 exampleData 中一个列表的下标,col 是该列表中你想访问的项的下标。在交互式环境中输入以下代码
>>> exampleData[0][0]
'4/5/2015 13:34'
>>> exampleData[0][1]
'Apples'
>>> exampleData[0][2]
'73'
>>> exampleData[1][1]
'Cherries'
>>> exampleData[6][1]
'Strawberries'
exampleData[0][0]进入第一个列表,并给出第一个字符串。 exampleData[0][2]进入第一个列表,并给出第三个字符串,以此类推。
在 for 循环中,从 Reader 对象读取数据
对于大型的 CSV 文件,你需要在一个 for 循环中使用 Reader 对象。这样避免将整个文件一次性装入内存。例如,在交互式环境中输入以下代码:
>>> import csv
>>> exampleFile = open('example.csv')
>>> exampleReader = csv.reader(exampleFile)
>>> for row in exampleReader:
print('Row #' + str(exampleReader.line_num) + ' ' + str(row))
Row #1 ['4/5/2015 13:34', 'Apples', '73']
Row #2 ['4/5/2015 3:41', 'Cherries', '85']
Row #3 ['4/6/2015 12:46', 'Pears', '14']
Row #4 ['4/8/2015 8:59', 'Oranges', '52']
Row #5 ['4/10/2015 2:07', 'Apples', '152']
Row #6 ['4/10/2015 18:10', 'Bananas', '23']
Row #7 ['4/10/2015 2:40', 'Strawberries', '98']
在导入 csv 模块,并从 CSV 文件得到 Reader 对象之后,可以循环遍历 Reader 对象中的行。每一行是一个值的列表,每个值表示一个单元格。print()函数将打印出当前行的编号以及该行的内容。要取得行号,就使用 Reader对象的 line_num 变量,它包含了当前行的编号。Reader 对象只能循环遍历一次。要再次读取 CSV 文件,必须调用 csv.reader,创建一个对象。
Writer 对象
Writer 对象让你将数据写入 CSV 文件。要创建一个 Writer 对象,就使用csv.writer()函数。在交互式环境中输入以下代码。
>>> import csv
>>> outputFile = open('output.csv', 'w', newline='')
>>> outputWriter = csv.writer(outputFile)
>>> outputWriter.writerow(['spam', 'eggs', 'bacon', 'ham'])
21
>>> outputWriter.writerow(['Hello, world!', 'eggs', 'bacon', 'ham'])
32
>>> outputWriter.writerow([1, 2, 3.141592, 4])
16
>>> outputFile.close()
首先,调用 open()并传入'w',以写模式打开一个文件。这将创建对象。然后将它传递给 csv.writer(),创建一个 Writer 对象。
在 Windows 上,需要为 open()函数的 newline 关键字参数传入一个空字符串。这样做的技术原因超出了本书的范围。如果忘记设置 newline 关键字参数, output.csv中的行距将有两倍,如图 14-1 所示。
Writer 对象的 writerow()方法接受一个列表参数。列表中的每个词,放在输出的CSV 文件中的一个单元格中。 writerow()函数的返回值,是写入文件中这一行的字符数(包括换行字符)。这段代码生成的文件像下面这样:
spam,eggs,bacon,ham
"Hello, world!",eggs,bacon,ham
1,2,3.141592,4
请注意, Writer 对象自动转义了'Hello, world!'中的逗号,在 CSV 文件中使用了双引号。模块 csv 让你不必自己处理这些特殊情况。
delimiter 和 lineterminator 关键字参数
假定你希望用制表符代替逗号来分隔单元格,并希望有两倍行距。可以在交互式环境中输入下面这样的代码:
>>> import csv
>>> csvFile = open('example.tsv', 'w', newline='')
>>> csvWriter = csv.writer(csvFile, delimiter='\t', lineterminator='\n\n')
>>> csvWriter.writerow(['apples', 'oranges', 'grapes'])
24
>>> csvWriter.writerow(['eggs', 'bacon', 'ham'])
17
>>> csvWriter.writerow(['spam', 'spam', 'spam', 'spam', 'spam', 'spam'])
32
>>> csvFile.close()
这改变了文件中的分隔符和行终止字符。分隔符是一行中单元格之间出现的字符。默认情况下, CSV 文件的分隔符是逗号。行终止字符是出现在行末的字符。默认情况下,行终止字符是换行符。你可以利用 csv.writer()的 delimiter 和 lineterminator关键字参数,将这些字符改成不同的值。传入 delimeter='\t'和 lineterminator='\n\n',这将单元格之间的字符改变为制表符,将行之间的字符改变为两个换行符。然后我们调用 writerow()三次,得到 3 行。这产生了文件 example.tsv,包含以下内容:
apples oranges grapes
eggs bacon ham
spam spam spam spam spam spam
既然单元格是由制表符分隔的,我们就使用文件扩展名.tsv,表示制表符分隔的值。