Python:如何读取和写入 CSV 文件

原文: https://thepythonguru.com/python-how-to-read-and-write-csv-files/


于 2020 年 1 月 7 日更新


什么是 CSV 文件?


CSV(逗号分隔值)是应用用来生成和使用数据的通用数据交换格式。 其他一些众所周知的数据交换格式是 XML,HTML,JSON 等。

CSV 文件是一个简单的文本文件,其中的每一行都包含用逗号分隔的值(或字段)列表。

尽管术语“逗号”本身出现在格式名称中,但是您会遇到 CSV 文件,其中使用制表符(\t)或管道(|)或任何其他可用作定界符的字符来分隔数据。

CSV 文件的第一行表示标题,该标题包含文件中的列名列表。 标头是可选的,但强烈建议使用。

CSV 文件通常用于表示表格数据。 例如,考虑下表:

ID 名称 国家代码 区域 人口
1 Kabul AFG Kabol 1780000
2 Qandahar AFG Qandahar 237500
3 Herat AFG Herat 186800
4 Mazar-e-Sharif AFG Balkh 127800
5 Amsterdam NLD Noord-Holland 731200

上表可以使用 CSV 格式表示如下:

  1. "ID","Name","CountryCode","District","Population"
  2. "1","Kabul","AFG","Kabol","1780000"
  3. "2","Qandahar","AFG","Qandahar","237500"
  4. "3","Herat","AFG","Herat","186800"
  5. "4","Mazar-e-Sharif","AFG","Balkh","127800"
  6. "5","Amsterdam","NLD","Noord-Holland","731200"

如果 CSV 文件中的值包含逗号,则必须将其用双引号引起来。 例如:

名称 年龄 地址
Jerry 10 2776 McDowell Street, Nashville, Tennessee
Tom 20 3171 Jessie Street, Westerville, Ohio
Mike 30 1818 Sherman Street, Hope, Kansas

要将逗号保留在“地址”字段中,请用双引号将其引起来,如下所示:

  1. Name,Age,Address
  2. Jerry,10,"2776 McDowell Street, Nashville, Tennessee"
  3. Tom,20,"3171 Jessie Street, Westerville, Ohio"
  4. Mike,30,"1818 Sherman Street, Hope, Kansas"

同样,如果您在字段中嵌入了双引号,则必须使用另一个双引号字符对其进行转义。 否则,将无法正确解释它们。 例如:

ID 用户 评论
1 Bob John said “Hello World”
2 Tom “The Magician”

要保留注释字段中的双引号,请使用两个双引号。

  1. Id,User,Comment
  2. 1,Bob,"John said ""Hello World"""
  3. 2,Tom,""The Magician""

重要的是要注意,CSV 格式尚未完全标准化。 因此,我们刚才提到的规则不是通用的。 有时,您会遇到 CSV 文件,它们以不同的方式表示字段。

幸运的是,为了使我们更轻松,Python 提供了csv模块。

在开始读写 CSV 文件之前,您应该对一般文件的使用方法有很好的了解。 如果需要复习,请考虑阅读如何在 Python 中读写文件。

csv模块用于读取和写入文件。 它主要提供以下类和函数:

  1. reader()
  2. writer()
  3. DictReader()
  4. DictWriter()

让我们从reader()函数开始。

使用reader()读取 CSV 文件


reader()函数接受一个文件对象,并返回一个_csv.reader对象,该对象可用于遍历 CSV 文件的内容。 reader()函数的语法如下:

语法reader(fileobj [, dialect='excel' [, **fmtparam] ]) -> _csv.reader

参数 描述
fileobj (必需)它引用文件对象
dialect (可选)方言是指格式化 CSV 文档的不同方式。 默认情况下,csv模块使用与 Microsoft Excel 相同的格式。 我们将在本文后面详细讨论方言。
fmtparam (可选)它是指一组用于自定义方言的关键字参数(请参阅下一节)。

假设我们有以下 CSV 文件:

employees.csv

  1. id,name,email,age,designation
  2. 1,John,john@mail.com,24,programmer
  3. 2,Bob,bob@mail.com,34,designer
  4. 3,Mary,mary@mail.com,43,sales

以下是读取此 CSV 文件的方法:

  1. import csv
  2. with open('employees.csv', 'rt') as f:
  3. csv_reader = csv.reader(f)
  4. for line in csv_reader:
  5. print(line)

预期输出

  1. ['id', 'name', 'email', 'age', 'designation']
  2. ['1', 'John', 'john@mail.com', '24', 'programmer']
  3. ['2', 'Bob', 'bob@mail.com', '34', 'designer']
  4. ['3', 'Mary', 'mary@mail.com', '43', 'sales']

请注意,CSV 文件中的每一行都作为字符串列表返回。

要从某些字段获取数据,可以使用索引。 例如:

  1. import csv
  2. with open('employees.csv', 'rt') as f:
  3. csv_reader = csv.reader(f)
  4. for line in csv_reader:
  5. print(line[0], line[1], line[2])

预期输出

  1. id name email
  2. 1 John john@mail.com
  3. 2 Bob bob@mail.com
  4. 3 Mary mary@mail.com

如果要跳过标题,请调用_csv.reader对象上的next()内置函数,然后照常遍历其余行。

  1. import csv
  2. with open('employees.csv', 'rt') as f:
  3. csv_reader = csv.reader(f)
  4. next(csv_reader) # skip the heading
  5. for line in csv_reader:
  6. print(line[0], line[1], line[2])

预期输出

  1. 1 John john@mail.com
  2. 2 Bob bob@mail.com
  3. 3 Mary mary@mail.com

自定义reader()


默认情况下,csv模块根据 Microsoft excel 使用的格式工作,但是您也可以使用方言定义自己的格式。

以下是一些其他参数,您可以将其传递给reader()函数以自定义其工作方式。

  • delimiter-是指用于分隔 CSV 文件中的值(或字段)的字符。 默认为逗号(,)。
  • skipinitialspace-控制分隔符后面的空格的解释方式。 如果为True,则将删除初始空格。 默认为False
  • lineterminator-指用于终止行的字符序列。 默认为\r\n
  • quotechar-指的是如果字段中出现特殊字符(如定界符),则将用于引用值的单个字符串。 默认为"
  • quoting-控制引号何时应由作者生成或由读者识别。 它可以采用以下常量之一:
    • csv.QUOTE_MINIMAL表示仅在需要时(例如,当字段包含quotechar或定界符时)添加引号。 这是默认值。
    • csv.QUOTE_ALL表示所有内容的引用,无论字段类型如何。
    • csv.QUOTE_NONNUMERIC表示除整数和浮点数外的所有内容。
    • csv.QUOTE_NONE表示在输出中不引用任何内容。 但是,在阅读报价时,字段值周围会包含引号。
  • escapechar-引用设置为QUOTE_NONE时,用于转义分隔符的单字符字符串。 默认为None
  • doublequote-控制字段内引号的处理。 当True时,两个连续的引号在读取期间被解释为一个,而在写入时,嵌入在数据中的每个引号字符都被写入两个引号。 让我们通过一些示例来更好地理解这些参数的工作方式:

定界符参数


employee_pipe.csv

  1. id|name|email|age|designation
  2. 1|John|john@mail.com|24|programmer
  3. 2|Bob|bob@mail.com|34|designer
  4. 3|Mary|mary@mail.com|43|sales

该文件使用竖线(|)字符作为分隔符。 以下是读取此 CSV 文件的方法:

  1. import csv
  2. with open('employees.csv', 'rt') as f:
  3. csv_reader = csv.reader(f, delimiter='|')
  4. for line in csv_reader:
  5. print(line)

预期输出

  1. ['id', 'name', 'email', 'age', 'designation']
  2. ['1', 'John', 'john@mail.com', '24', 'programmer']
  3. ['2', 'Bob', 'bob@mail.com', '34', 'designer']
  4. ['3', 'Mary', 'mary@mail.com', '43', 'sales']

skipinitialspace参数


Basketball_players.csv

  1. "Name", "Team", "Position", "Height(inches)", "Weight(lbs)", "Age"
  2. "Adam Donachie", "BAL", "Catcher", 74, 180, 22.99
  3. "Paul Bako", "BAL", "Catcher", 74, 215, 34.69
  4. "Ramon Hernandez", "BAL", "Catcher", 72, 210, 30.78
  5. "Kevin Millar", "BAL", "First Baseman", 72, 210, 35.43
  6. "Chris Gomez", "BAL", "First Baseman", 73, 188, 35.71
  7. "Brian Roberts", "BAL", "Second Baseman", 69, 176, 29.39
  8. "Miguel Tejada", "BAL", "Shortstop", 69, 209, 30.77
  9. "Melvin Mora", "BAL", "Third Baseman", 71, 200, 35.07

该 CSV 文件在逗号(,)后包含空格。 要正确读取此 CSV 文件,请将skipinitialspace设置为True,如下所示:

  1. import csv
  2. with open('baseball_players.csv', 'rt') as f:
  3. csv_reader = csv.reader(f, skipinitialspace=True)
  4. for line in csv_reader:
  5. print(line)

预期输出

  1. ['Name', 'Team', 'Position', 'Height(inches)', 'Weight(lbs)', 'Age']
  2. ['Adam Donachie', 'BAL', 'Catcher', '74', '180', '22.99']
  3. ['Paul Bako', 'BAL', 'Catcher', '74', '215', '34.69']
  4. ['Ramon Hernandez', 'BAL', 'Catcher', '72', '210', '30.78']
  5. ['Kevin Millar', 'BAL', 'First Baseman', '72', '210', '35.43']
  6. ['Chris Gomez', 'BAL', 'First Baseman', '73', '188', '35.71']
  7. ['Brian Roberts', 'BAL', 'Second Baseman', '69', '176', '29.39']
  8. ['Miguel Tejada', 'BAL', 'Shortstop', '69', '209', '30.77']
  9. ['Melvin Mora', 'BAL', 'Third Baseman', '71', '200', '35.07']

quotechar参数


address.csv

  1. Name, Age, Address
  2. Jerry, 44, '2776 McDowell Street, Nashville, Tennessee'
  3. Tom, 21, '3171 Jessie Street, Westerville, Ohio'
  4. Mike, 32, '1818 Sherman Street, Hope, Kansas'

此文件中有两件事需要注意。 首先,使用单引号(')而不是"双引号(这是默认值)包装地址字段。 其次,逗号(,)后面有空格。

如果尝试在不更改引号的情况下读取此文件,则将获得以下输出:

  1. import csv
  2. with open('addresses.csv', 'rt') as f:
  3. csv_reader = csv.reader(f, skipinitialspace=True)
  4. for line in csv_reader:
  5. print(line)

预期输出

  1. ['Name', 'Age', 'Address']
  2. ['Jerry', '44', "'2776 McDowell Street", 'Nashville', "Tennessee'"]
  3. ['Tom', '21', "'3171 Jessie Street", 'Westerville', "Ohio'"]
  4. ['Mike', '32', "'1818 Sherman Street", 'Hope', "Kansas'"]

请注意,地址分为三个字段,这当然是不正确的。 要解决此问题,只需使用quotechar参数将引号字符更改为单引号('):

  1. import csv
  2. with open('housing.csv', 'rt') as f:
  3. csv_reader = csv.reader(f, skipinitialspace=True, quotechar="'")
  4. for line in csv_reader:
  5. print(line)

预期输出

  1. ['Name', 'Age', 'Address']
  2. ['Jerry', '44', '2776 McDowell Street, Nashville, Tennessee']
  3. ['Tom', '21', '3171 Jessie Street, Westerville, Ohio']
  4. ['Mike', '32', '1818 Sherman Street, Hope, Kansas']

escapechar参数


comments.csv

  1. Id, User, Comment
  2. 1, Bob, "John said \"Hello World\""
  3. 2, Tom, "\"The Magician\""
  4. 3, Harry, "\"walk around the corner\" she explained to the child"
  5. 4, Louis, "He said, \"stop pulling the dog's tail\""

此文件使用反斜杠(\)字符转义嵌入的双引号。 但是,默认情况下,默认的csv模块使用双引号字符转义双引号字符。

如果尝试使用默认选项读取此文件,则将获得如下输出:

  1. import csv
  2. with open('employees.csv', 'rt') as f:
  3. csv_reader = csv.reader(f, skipinitialspace=True)
  4. for line in csv_reader:
  5. print(line)

预期输出

  1. ['Id', 'User', 'Comment']
  2. ['1', 'Bob', 'John said \\Hello World\\""']
  3. ['2', 'Tom', '\\The Magician\\""']
  4. ['3', 'Harry', '\\walk around the corner\\" she explained to the child"']
  5. ['4', 'Louis', 'He said, \\stop pulling the dog\'s tail\\""']

这个输出肯定是不希望的。 要获得正确的输出,请使用escapechar参数更改转义符,如下所示:

  1. import csv
  2. with open('employees.csv', 'rt') as f:
  3. csv_reader = csv.reader(f, skipinitialspace=True, escapechar='\\')
  4. for line in csv_reader:
  5. print(line)

预期输出

  1. ['Id', 'User', 'Comment']
  2. ['1', 'Bob', 'John said "Hello World"']
  3. ['2', 'Tom', '"The Magician"']
  4. ['3', 'Harry', '"walk around the corner" she explained to the child']
  5. ['4', 'Louis', 'He said, "stop pulling the dog\'s tail"']

doublequote参数


dialogs.csv

  1. Id, Actor, Dialogue
  2. 1, Harley Betts, "The suspect told the arresting officer, ""I was nowhere near the crime."""
  3. 2, Clyde Esparza, "John said, ""I have just finished reading Browning's 'My Last Duchess.'"""
  4. 3, Zack Campbell, "Bill asked Sandra, ""Will you marry me?"""
  5. 4, Keziah Chaney, "The librarian whispered to us, ""The sign on the wall says 'Quiet'"""

此文件使用双引号转义字段中嵌入的双引号字符。 默认情况下,doublequote设置为True。 结果,在读取两个连续的双引号时会被解释为一个。

  1. import csv
  2. with open('employees.csv', 'rt') as f:
  3. # same as csv_reader = csv.reader(f, skipinitialspace=True)
  4. csv_reader = csv.reader(f, skipinitialspace=True, doublequote=True)
  5. for line in csv_reader:
  6. print(line)

预期输出

  1. ['Id', 'Actor', 'Dialogue']
  2. ['1', 'Harley Betts', 'The suspect told the arresting officer, "I was nowhere near the crime."']
  3. ['2', 'Clyde Esparza', 'John said, "I have just finished reading Browning\'s \'My Last Duchess.\'"']
  4. ['3', 'Zack Campbell', 'Bill asked Sandra, "Will you marry me?"']
  5. ['4', 'Keziah Chaney', 'The librarian whispered to us, "The sign on the wall says \'Quiet\'"']

但是,如果将doublequote设置为False,则连续的双引号将出现在输出中。

  1. import csv
  2. with open('employees.csv', 'rt') as f:
  3. csv_reader = csv.reader(f, skipinitialspace=True, doublequote=False)
  4. for line in csv_reader:
  5. print(line)

预期输出

  1. ['Id', 'Actor', 'Dialogue']
  2. ['1', 'Harley Betts', 'The suspect told the arresting officer, "I was nowhere near the crime."""']
  3. ['2', 'Clyde Esparza', 'John said, "I have just finished reading Browning\'s \'My Last Duchess.\'"""']
  4. ['3', 'Zack Campbell', 'Bill asked Sandra, "Will you marry me?"""']
  5. ['4', 'Keziah Chaney', 'The librarian whispered to us, "The sign on the wall says \'Quiet\'"""']

writer()编写 CSV 文件


要将数据写入 CSV 文件,我们使用writer()函数。 它接受与reader()函数相同的参数,但返回writer对象(即_csv.writer):

语法writer(fileobj [, dialect='excel' [, **fmtparam] ]) -> csv_writer

参数 描述
fileobj (必需)它引用文件对象
dialect (可选)方言是指格式化 CSV 文档的不同方式。 默认情况下,csv模块使用与 Microsoft Excel 相同的格式。 我们将在本文后面详细讨论方言。
fmtparam (可选)格式化参数,与reader()的功能相同。

writer实例提供以下两种写入数据的方法:

方法 描述
writerow(row) 写入一行数据并返回写入的字符数。 row必须是字符串和数字的序列。
writerows(rows) 写入多行数据并返回Nonerows必须是一个序列。

以下是示例:

示例 1 :使用writerow()

  1. import csv
  2. header = ['id', 'name', 'address', 'zip']
  3. rows = [
  4. [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ],
  5. [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ],
  6. [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704],
  7. [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677],
  8. [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257],
  9. ]
  10. with open('customers.csv', 'wt') as f:
  11. csv_writer = csv.writer(f)
  12. csv_writer.writerow(header) # write header
  13. for row in rows:
  14. csv_writer.writerow(row)

示例 2 :使用writerows()

  1. import csv
  2. header = ['id', 'name', 'address', 'zip']
  3. rows = [
  4. [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ],
  5. [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ],
  6. [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704],
  7. [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677],
  8. [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257],
  9. ]
  10. with open('customers.csv', 'wt') as f:
  11. csv_writer = csv.writer(f)
  12. csv_writer.writerow(header) # write header
  13. csv_writer.writerows(rows)

这两个清单生成的输出将是相同的,看起来像这样:

customer.csv

  1. id,name,address,zip
  2. 1,Hannah,"4891 Blackwell Street, Anchorage, Alaska",99503
  3. 2,Walton,"4223 Half and Half Drive, Lemoore, California",97401
  4. 3,Sam,"3952 Little Street, Akron, Ohio",93704
  5. 4,Chris,"3192 Flinderation Road, Arlington Heights, Illinois",62677
  6. 5,Doug,"3236 Walkers Ridge Way, Burr Ridge",61257

注意,只有地址字段用双引号引起来。 这是因为默认情况下quoting参数设置为QUOTE_MINIMAL。 换句话说,仅当quotechar或定界符出现在数据中时,字段才会被引用。

假设您要在所有文本数据中使用双引号。 为此,请将quoting参数设置为QUOTE_NONNUMERIC

  1. import csv
  2. header = ['id', 'name', 'address', 'zip']
  3. rows = [
  4. [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ],
  5. [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ],
  6. [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704],
  7. [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677],
  8. [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257],
  9. ]
  10. with open('customers.csv', 'wt') as f:
  11. csv_writer = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC)
  12. csv_writer.writerow(header) # write header
  13. csv_writer.writerows(rows)

预期输出

customers.csv

  1. "id","name","address","zip"
  2. 1,"Hannah","4891 Blackwell Street, Anchorage, Alaska",99503
  3. 2,"Walton","4223 Half and Half Drive, Lemoore, California",97401
  4. 3,"Sam","3952 Little Street, Akron, Ohio",93704
  5. 4,"Chris","3192 Flinderation Road, Arlington Heights, Illinois",62677
  6. 5,"Doug","3236 Walkers Ridge Way, Burr Ridge",61257

现在,所有名称和地址都用双引号引起来。

如果要在所有字段周围加双引号,而不管数据中是否出现quotechar或定界符,请将quoting设置为csv.QUOTE_ALL

  1. import csv
  2. header = ['id', 'name', 'address', 'zip']
  3. rows = [
  4. [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ],
  5. [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ],
  6. [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704],
  7. [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677],
  8. [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257],
  9. ]
  10. with open('customers.csv', 'wt') as f:
  11. csv_writer = csv.writer(f, quoting=csv.QUOTE_ALL)
  12. csv_writer.writerow(header) # write header
  13. csv_writer.writerows(rows)

预期输出

  1. "id","name","address","zip"
  2. "1","Hannah","4891 Blackwell Street, Anchorage, Alaska","99503"
  3. "2","Walton","4223 Half and Half Drive, Lemoore, California","97401"
  4. "3","Sam","3952 Little Street, Akron, Ohio","93704"
  5. "4","Chris","3192 Flinderation Road, Arlington Heights, Illinois","62677"
  6. "5","Doug","3236 Walkers Ridge Way, Burr Ridge","61257"

现在一切都被双引号了。

重要的是要注意,当引号打开时(即quoting参数的值不是csv.QUOTE_NONE),csv模块将使用quotechar(默认为")对字段进行引号。

下面的清单将引号字符从双引号(")更改为单引号(')。

  1. import csv
  2. header = ['id', 'name', 'address', 'zip']
  3. rows = [
  4. [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ],
  5. [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ],
  6. [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704],
  7. [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677],
  8. [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257],
  9. ]
  10. with open('customers.csv', 'wt') as f:
  11. csv_writer = csv.writer(f, quotechar="'")
  12. csv_writer.writerow(header) # write header
  13. csv_writer.writerows(rows)

预期输出

  1. id,name,address,zip
  2. 1,Hannah,'4891 Blackwell Street, Anchorage, Alaska',99503
  3. 2,Walton,'4223 Half and Half Drive, Lemoore, California',97401
  4. 3,Sam,'3952 Little Street, Akron, Ohio',93704
  5. 4,Chris,'3192 Flinderation Road, Arlington Heights, Illinois',62677
  6. 5,Doug,'3236 Walkers Ridge Way, Burr Ridge',61257

在这种情况下,csv模块使用单引号(')而不是(")来引用包含 quotechar 或定界符的字段。

我们也可以通过将quoting设置为csv.QUOTE_NONE来关闭全部引用。 但是,如果这样做,并且分隔符出现在数据中,则将出现如下错误:

  1. import csv
  2. header = ['id', 'name', 'address', 'zip']
  3. rows = [
  4. [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ],
  5. [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ],
  6. [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704],
  7. [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677],
  8. [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257],
  9. ]
  10. with open('customers.csv', 'wt') as f:
  11. csv_writer = csv.writer(f, quoting=csv.QUOTE_NONE)
  12. csv_writer.writerow(header) # write header
  13. csv_writer.writerows(rows)

预期输出

  1. Traceback (most recent call last):
  2. ...
  3. csv_writer.writerows(rows)
  4. _csv.Error: need to escape, but no escapechar set

问题在于地址字段包含嵌入式逗号(,),并且由于我们已关闭了引用字段的功能,因此csv模块不知道如何正确对其进行转义。

这是escapechar参数起作用的地方。 它使用一个单字符字符串,当引号关闭时(即quoting=csv.QUOTE_NONE),该字符串将用于转义分隔符。

以下清单将escapechar设置为反斜杠(\)。

  1. import csv
  2. header = ['id', 'name', 'address', 'zip']
  3. rows = [
  4. [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ],
  5. [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ],
  6. [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704],
  7. [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677],
  8. [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257],
  9. ]
  10. with open('customers.csv', 'wt') as f:
  11. csv_writer = csv.writer(f, quoting=csv.QUOTE_NONE, escapechar='\\')
  12. csv_writer.writerow(header) # write header
  13. csv_writer.writerows(rows)

预期输出

  1. id,name,address,zip
  2. 1,Hannah,4891 Blackwell Street\, Anchorage\, Alaska,99503
  3. 2,Walton,4223 Half and Half Drive\, Lemoore\, California,97401
  4. 3,Sam,3952 Little Street\, Akron\, Ohio,93704
  5. 4,Chris,3192 Flinderation Road\, Arlington Heights\, Illinois,62677
  6. 5,Doug,3236 Walkers Ridge Way\, Burr Ridge,61257

请注意,地址字段中的逗号(,)使用反斜杠(\)字符进行转义。

现在,您应该对reader()writer()函数使用的各种格式参数及其上下文有很好的了解。 在下一节中,将看到其他一些读取和写入数据的方式。

使用DictReader读取 CSV 文件


DictReader的工作原理几乎与reader()相似,但是它不是将行重新调谐为列表,而是返回字典。 其语法如下:

语法::DictReader(fileobj, fieldnames=None, restkey=None, restval=None, dialect='excel', **fmtparam)

参数 描述
fileobj (必需)引用文件对象。
fieldnames (可选)它是指将按顺序在返回的字典中使用的键的列表。 如果省略,则从 CSV 文件的第一行推断字段名称。
restkey (可选)如果行中的字段比fieldnames参数中指定的字段多,则其余字段将作为由restkey参数的值键控的序列存储。
restval (可选)它为输入中缺少的字段提供值。
dialect (可选)方言是指格式化 CSV 文档的不同方式。 默认情况下,csv模块使用与 Microsoft excel 相同的格式。 我们将在本文后面详细讨论方言。
fmtparam 它指的是格式化参数,并且与reader()writer()完全一样。

让我们举一些例子:

示例 1

customers.csv

  1. id,name,address,zip
  2. 1,Hannah,4891 Blackwell Street\, Anchorage\, Alaska,99503
  3. 2,Walton,4223 Half and Half Drive\, Lemoore\, California,97401
  4. 3,Sam,3952 Little Street\, Akron\, Ohio,93704
  5. 4,Chris,3192 Flinderation Road\, Arlington Heights\, Illinois,62677
  6. 5,Doug,3236 Walkers Ridge Way\, Burr Ridge,61257
  1. import csv
  2. with open('customers.csv', 'rt') as f:
  3. csv_reader = csv.DictReader(f, escapechar='\\')
  4. for row in csv_reader:
  5. print(row)

预期输出

  1. {'id': '1', 'name': 'Hannah', 'zip': '99503', 'address': '4891 Blackwell Street, Anchorage, Alaska'}
  2. {'id': '2', 'name': 'Walton', 'zip': '97401', 'address': '4223 Half and Half Drive, Lemoore, California'}
  3. {'id': '3', 'name': 'Sam', 'zip': '93704', 'address': '3952 Little Street, Akron, Ohio'}
  4. {'id': '4', 'name': 'Chris', 'zip': '62677', 'address': '3192 Flinderation Road, Arlington Heights, Illinois'}
  5. {'id': '5', 'name': 'Doug', 'zip': '61257', 'address': '3236 Walkers Ridge Way, Burr Ridge'}

注意:结果中键的顺序可能会有所不同。 由于字典不保留元素的顺序。

在这种情况下,字段名称是从 CSV 文件的第一行(或标题)推断出来的。

示例 2 :使用fieldnames参数

  1. 1,Hannah,4891 Blackwell Street\, Anchorage\, Alaska,99503
  2. 2,Walton,4223 Half and Half Drive\, Lemoore\, California,97401
  3. 3,Sam,3952 Little Street\, Akron\, Ohio,93704
  4. 4,Chris,3192 Flinderation Road\, Arlington Heights\, Illinois,62677
  5. 5,Doug,3236 Walkers Ridge Way\, Burr Ridge,61257

此 CSV 文件没有标题。 因此,我们必须通过fieldnames参数提供字段名称。

  1. import csv
  2. with open('customers.csv', 'rt') as f:
  3. fields = ['id', 'name', 'address', 'zip']
  4. csv_reader = csv.DictReader(f, fieldnames=fields, escapechar='\\')
  5. for row in csv_reader:
  6. print(row)

预期输出

  1. {'name': 'Hannah', 'zip': '99503', 'id': '1', 'address': '4891 Blackwell Street, Anchorage, Alaska'}
  2. {'name': 'Walton', 'zip': '97401', 'id': '2', 'address': '4223 Half and Half Drive, Lemoore, California'}
  3. {'name': 'Sam', 'zip': '93704', 'id': '3', 'address': '3952 Little Street, Akron, Ohio'}
  4. {'name': 'Chris', 'zip': '62677', 'id': '4', 'address': '3192 Flinderation Road, Arlington Heights, Illinois'}
  5. {'name': 'Doug', 'zip': '61257', 'id': '5', 'address': '3236 Walkers Ridge Way, Burr Ridge'}

示例 3 :使用restkey参数

  1. 1,Hannah,4891 Blackwell Street\, Anchorage\, Alaska,99503
  2. 2,Walton,4223 Half and Half Drive\, Lemoore\, California,97401
  3. 3,Sam,3952 Little Street\, Akron\, Ohio,93704
  4. 4,Chris,3192 Flinderation Road\, Arlington Heights\, Illinois,62677
  5. 5,Doug,3236 Walkers Ridge Way\, Burr Ridge,61257
  1. import csv
  2. with open('customers.csv', 'rt') as f:
  3. fields = ['id','name',]
  4. csv_reader = csv.DictReader(f, fieldnames=fields, restkey='extra', escapechar='\\')
  5. for row in csv_reader:
  6. print(row)

预期输出

  1. {'id': '1', 'name': 'Hannah', 'extra': ['4891 Blackwell Street, Anchorage, Alaska', '99503']}
  2. {'id': '2', 'name': 'Walton', 'extra': ['4223 Half and Half Drive, Lemoore, California', '97401']}
  3. {'id': '3', 'name': 'Sam', 'extra': ['3952 Little Street, Akron, Ohio', '93704']}
  4. {'id': '4', 'name': 'Chris', 'extra': ['3192 Flinderation Road, Arlington Heights, Illinois', '62677']}
  5. {'id': '5', 'name': 'Doug', 'extra': ['3236 Walkers Ridge Way, Burr Ridge', '61257']}

请注意,地址和邮政编码现在存储为由值extra键控的序列。

示例 4 :使用restval

  1. 1,Hannah,4891 Blackwell Street\, Anchorage\, Alaska,99503
  2. 2,Walton,4223 Half and Half Drive\, Lemoore\, California,97401
  3. 3,Sam,3952 Little Street\, Akron\, Ohio,93704
  4. 4,Chris,3192 Flinderation Road\, Arlington Heights\, Illinois,62677
  5. 5,Doug,3236 Walkers Ridge Way\, Burr Ridge,61257
  1. import csv
  2. with open('customers.csv', 'rt') as f:
  3. fields = ['id','name', 'address', 'zip', 'phone', 'email'] # two extra fields
  4. csv_reader = csv.DictReader(f, fieldnames=fields, restkey='extra', restval='NA', escapechar='\\')
  5. for row in csv_reader:
  6. print(row)

预期输出

  1. {'id': '1', 'name': 'Hannah', 'email': 'NA', 'phone': 'NA', 'address': '4891 Blackwell Street, Anchorage, Alaska', 'zip': '99503'}
  2. {'id': '2', 'name': 'Walton', 'email': 'NA', 'phone': 'NA', 'address': '4223 Half and Half Drive, Lemoore, California', 'zip': '97401'}
  3. {'id': '3', 'name': 'Sam', 'email': 'NA', 'phone': 'NA', 'address': '3952 Little Street, Akron, Ohio', 'zip': '93704'}
  4. {'id': '4', 'name': 'Chris', 'email': 'NA', 'phone': 'NA', 'address': '3192 Flinderation Road, Arlington Heights, Illinois', 'zip': '62677'}
  5. {'id': '5', 'name': 'Doug', 'email': 'NA', 'phone': 'NA', 'address': '3236 Walkers Ridge Way, Burr Ridge', 'zip': '61257'}

在这种情况下,我们为字段指定了两个额外的字段:phoneemail。 额外字段的值由restval参数提供。

DictWriter()编写 CSV 文件


DictWriter对象将字典写入 CSV 文件。 其语法如下:

语法DictWriter(fileobj, fieldnames, restval='', extrasaction='raise', dialect='excel', **fmtparam)

参数 描述
fileobj 它引用文件对象
fieldnames 它指的是字段名称以及将它们写入文件的顺序。
restval 它提供了字典中不存在的键的缺失值。
extrasaction 它控制如果字典包含fieldnames参数中找不到的键,该采取的动作。 默认情况下,extrasaction设置为raise,这意味着将在此类事件中引发异常。 如果要忽略其他值,请将extrasaction设置为ignore

DictWriter提供以下三种写入数据的方法。

方法 描述
writeheader() 将标头(即fieldnames)写入 CSV 文件并返回None
writerow(row) 写入一行数据并返回写入的字符数。 row必须是字符串和数字的序列。
writerows(rows) 写入多行数据并返回Nonerows必须是一个序列。

Let’s take some examples:

示例 1

  1. import csv
  2. header = ['id', 'name', 'address', 'zip']
  3. rows = [
  4. {'id': 1, 'name': 'Hannah', 'address': '4891 Blackwell Street, Anchorage, Alaska', 'zip': 99503 },
  5. {'id': 2, 'name': 'Walton', 'address': '4223 Half and Half Drive, Lemoore, California', 'zip': 97401 },
  6. {'id': 3, 'name': 'Sam', 'address': '3952 Little Street, Akron, Ohio', 'zip': 93704 },
  7. {'id': 4, 'name': 'Chris', 'address': '3192 Flinderation Road, Arlington Heights, Illinois', 'zip': 62677},
  8. {'id': 5, 'name': 'Doug', 'address': '3236 Walkers Ridge Way, Burr Ridge', 'zip': 61257},
  9. ]
  10. with open('dictcustomers.csv', 'wt') as f:
  11. csv_writer = csv.DictWriter(f, fieldnames=header)
  12. csv_writer.writeheader() # write header
  13. csv_writer.writerows(rows)

预期输出

dictcustomers.csv

  1. id,name,address,zip
  2. 1,Hannah,"4891 Blackwell Street, Anchorage, Alaska",99503
  3. 2,Walton,"4223 Half and Half Drive, Lemoore, California",97401
  4. 3,Sam,"3952 Little Street, Akron, Ohio",93704
  5. 4,Chris,"3192 Flinderation Road, Arlington Heights, Illinois",62677
  6. 5,Doug,"3236 Walkers Ridge Way, Burr Ridge",61257

示例 2 :使用restval

  1. import csv
  2. header = ['id', 'name', 'address', 'zip', 'email'] # an extra field email
  3. rows = [
  4. {'id': 1, 'name': 'Hannah', 'address': '4891 Blackwell Street, Anchorage, Alaska', 'zip': 99503 },
  5. {'id': 2, 'name': 'Walton', 'address': '4223 Half and Half Drive, Lemoore, California', 'zip': 97401 },
  6. {'id': 3, 'name': 'Sam', 'address': '3952 Little Street, Akron, Ohio', 'zip': 93704 },
  7. {'id': 4, 'name': 'Chris', 'address': '3192 Flinderation Road, Arlington Heights, Illinois', 'zip': 62677},
  8. {'id': 5, 'name': 'Doug', 'address': '3236 Walkers Ridge Way, Burr Ridge', 'zip': 61257},
  9. ]
  10. with open('dictcustomers.csv', 'wt') as f:
  11. csv_writer = csv.DictWriter(f, fieldnames=header, restval="NA")
  12. csv_writer.writeheader() # write header
  13. csv_writer.writerows(rows)

预期输出

dictcustomers.csv

  1. id,name,address,zip,email
  2. 1,Hannah,"4891 Blackwell Street, Anchorage, Alaska",99503,NA
  3. 2,Walton,"4223 Half and Half Drive, Lemoore, California",97401,NA
  4. 3,Sam,"3952 Little Street, Akron, Ohio",93704,NA
  5. 4,Chris,"3192 Flinderation Road, Arlington Heights, Illinois",62677,NA
  6. 5,Doug,"3236 Walkers Ridge Way, Burr Ridge",61257,NA

在这种情况下,字典中缺少email字段的值。 结果,restval的值将用于email字段。

示例 3 :使用extrasaction

  1. import csv
  2. header = ['id', 'name', 'address'] # notice zip is missing
  3. rows = [
  4. {'id': 1, 'name': 'Hannah', 'address': '4891 Blackwell Street, Anchorage, Alaska', 'zip': 99503 },
  5. {'id': 2, 'name': 'Walton', 'address': '4223 Half and Half Drive, Lemoore, California', 'zip': 97401 },
  6. {'id': 3, 'name': 'Sam', 'address': '3952 Little Street, Akron, Ohio', 'zip': 93704 },
  7. {'id': 4, 'name': 'Chris', 'address': '3192 Flinderation Road, Arlington Heights, Illinois', 'zip': 62677},
  8. {'id': 5, 'name': 'Doug', 'address': '3236 Walkers Ridge Way, Burr Ridge', 'zip': 61257},
  9. ]
  10. with open('dictcustomers.csv', 'wt') as f:
  11. csv_writer = csv.DictWriter(
  12. f,
  13. fieldnames=header,
  14. restval="NA",
  15. extrasaction='ignore' # ignore extra values in the dictionary
  16. )
  17. csv_writer.writeheader() # write header
  18. csv_writer.writerows(rows)

在此,词典包含一个名为zip的附加键,该键不在header列表中。 为了防止引发异常,我们将extrasaction设置为ignore

预期输出

dictcustomers.csv

  1. id,name,address
  2. 1,Hannah,"4891 Blackwell Street, Anchorage, Alaska"
  3. 2,Walton,"4223 Half and Half Drive, Lemoore, California"
  4. 3,Sam,"3952 Little Street, Akron, Ohio"
  5. 4,Chris,"3192 Flinderation Road, Arlington Heights, Illinois"
  6. 5,Doug,"3236 Walkers Ridge Way, Burr Ridge"

创建方言


在本文的前面,我们学习了各种格式化参数,这些参数使我们可以自定义readerwriter对象,以适应 CSV 约定中的差异。

如果发现自己一次又一次地传递相同的格式参数集。 考虑创建自己的方言。

方言对象或(仅是方言)是对各种格式参数进行分组的一种方式。 一旦创建了方言对象,只需将其传递给阅读器或书写器,而不必分别传递每个格式参数。

要创建新的方言,我们使用register_dialect()函数。 它接受方言名称作为字符串,并接受一个或多个格式参数作为关键字参数。

下表列出了所有格式参数及其默认值:

参数 默认值 描述
delimiter , 它是指用于分隔 CSV 文件中的值(或字段)的字符。
skipinitialspace False 它控制定界符后面的空格的解释方式。 如果为True,则将删除初始空格。
lineterminator \r\n 它是指用于终止行的字符序列。
quotechar " 它指的是如果字段中出现特殊字符(如定界符),则将用于引用值的单个字符串。
quoting csv.QUOTE_NONE 控制引号由作者生成或由读者识别的时间(其他选项请参见上文)。
escapechar None 引用设置为引号时,它用于转义定界符的一字符字符串。
doublequote True 控制字段内引号的处理。 当True时,在读取期间将两个连续的引号解释为一个,而在写入时,将嵌入数据中的每个引号字符写入为两个引号。

让我们创建一个简单的方言。

  1. import csv
  2. # create and register new dialect
  3. csv.register_dialect('psv', delimiter='|', quoting=csv.QUOTE_NONNUMERIC)
  4. header = ['id', 'year', 'age', 'name', 'movie']
  5. rows = [
  6. {'id': 1, 'year': 2013, 'age': 55, 'name': "Daniel Day-Lewis", 'movie': "Lincoln" },
  7. {'id': 2, 'year': 2014, 'age': 44, 'name': "Matthew McConaughey", 'movie': "Dallas Buyers Club" },
  8. {'id': 3, 'year': 2015, 'age': 33, 'name': "Eddie Redmayne", 'movie': "The Theory of Everything" },
  9. {'id': 4, 'year': 2016, 'age': 41, 'name': "Leonardo DiCaprio", 'movie': "The Revenant" }
  10. ]
  11. with open('oscars.csv', 'wt') as f:
  12. csv_writer = csv.DictWriter(
  13. f,
  14. fieldnames=header,
  15. dialect='psv', # pass the new dialect
  16. extrasaction='ignore'
  17. )
  18. csv_writer.writeheader() # write header
  19. csv_writer.writerows(rows)

预期输出

oscars.csv

  1. "id"|"year"|"age"|"name"|"movie"
  2. 1|2013|55|"Daniel Day-Lewis"|"Lincoln"
  3. 2|2014|44|"Matthew McConaughey"|"Dallas Buyers Club"
  4. 3|2015|33|"Eddie Redmayne"|"The Theory of Everything"
  5. 4|2016|41|"Leonardo DiCaprio"|"The Revenant"