ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# Python:如何读取和写入 CSV 文件 > 原文: [https://thepythonguru.com/python-how-to-read-and-write-csv-files/](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 格式表示如下: ```py "ID","Name","CountryCode","District","Population" "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 文件中的值包含逗号,则必须将其用双引号引起来。 例如: | 名称 | 年龄 | 地址 | | --- | --- | --- | | Jerry | 10 | 2776 McDowell Street, Nashville, Tennessee | | Tom | 20 | 3171 Jessie Street, Westerville, Ohio | | Mike | 30 | 1818 Sherman Street, Hope, Kansas | 要将逗号保留在“地址”字段中,请用双引号将其引起来,如下所示: ```py Name,Age,Address Jerry,10,"2776 McDowell Street, Nashville, Tennessee" Tom,20,"3171 Jessie Street, Westerville, Ohio" Mike,30,"1818 Sherman Street, Hope, Kansas" ``` 同样,如果您在字段中嵌入了双引号,则必须使用另一个双引号字符对其进行转义。 否则,将无法正确解释它们。 例如: | ID | 用户 | 评论 | | --- | --- | --- | | 1 | Bob | John said "Hello World" | | 2 | Tom | "The Magician" | 要保留注释字段中的双引号,请使用两个双引号。 ```py Id,User,Comment 1,Bob,"John said ""Hello World""" 2,Tom,""The Magician"" ``` 重要的是要注意,CSV 格式尚未完全标准化。 因此,我们刚才提到的规则不是通用的。 有时,您会遇到 CSV 文件,它们以不同的方式表示字段。 幸运的是,为了使我们更轻松,Python 提供了`csv`模块。 在开始读写 CSV 文件之前,您应该对一般文件的使用方法有很好的了解。 如果需要复习,请考虑阅读[如何在 Python](/python-how-to-read-and-write-files/) 中读写文件。 `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` ```py id,name,email,age,designation 1,John,john@mail.com,24,programmer 2,Bob,bob@mail.com,34,designer 3,Mary,mary@mail.com,43,sales ``` 以下是读取此 CSV 文件的方法: ```py import csv with open('employees.csv', 'rt') as f: csv_reader = csv.reader(f) for line in csv_reader: print(line) ``` **预期输出**: ```py ['id', 'name', 'email', 'age', 'designation'] ['1', 'John', 'john@mail.com', '24', 'programmer'] ['2', 'Bob', 'bob@mail.com', '34', 'designer'] ['3', 'Mary', 'mary@mail.com', '43', 'sales'] ``` 请注意,CSV 文件中的每一行都作为字符串列表返回。 要从某些字段获取数据,可以使用索引。 例如: ```py import csv with open('employees.csv', 'rt') as f: csv_reader = csv.reader(f) for line in csv_reader: print(line[0], line[1], line[2]) ``` **预期输出**: ```py id name email 1 John john@mail.com 2 Bob bob@mail.com 3 Mary mary@mail.com ``` 如果要跳过标题,请调用`_csv.reader`对象上的`next()`内置函数,然后照常遍历其余行。 ```py import csv with open('employees.csv', 'rt') as f: csv_reader = csv.reader(f) next(csv_reader) # skip the heading for line in csv_reader: print(line[0], line[1], line[2]) ``` **预期输出**: ```py 1 John john@mail.com 2 Bob bob@mail.com 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`** ```py id|name|email|age|designation 1|John|john@mail.com|24|programmer 2|Bob|bob@mail.com|34|designer 3|Mary|mary@mail.com|43|sales ``` 该文件使用竖线(`|`)字符作为分隔符。 以下是读取此 CSV 文件的方法: ```py import csv with open('employees.csv', 'rt') as f: csv_reader = csv.reader(f, delimiter='|') for line in csv_reader: print(line) ``` **预期输出**: ```py ['id', 'name', 'email', 'age', 'designation'] ['1', 'John', 'john@mail.com', '24', 'programmer'] ['2', 'Bob', 'bob@mail.com', '34', 'designer'] ['3', 'Mary', 'mary@mail.com', '43', 'sales'] ``` ### `skipinitialspace`参数 * * * **`Basketball_players.csv`** ```py "Name", "Team", "Position", "Height(inches)", "Weight(lbs)", "Age" "Adam Donachie", "BAL", "Catcher", 74, 180, 22.99 "Paul Bako", "BAL", "Catcher", 74, 215, 34.69 "Ramon Hernandez", "BAL", "Catcher", 72, 210, 30.78 "Kevin Millar", "BAL", "First Baseman", 72, 210, 35.43 "Chris Gomez", "BAL", "First Baseman", 73, 188, 35.71 "Brian Roberts", "BAL", "Second Baseman", 69, 176, 29.39 "Miguel Tejada", "BAL", "Shortstop", 69, 209, 30.77 "Melvin Mora", "BAL", "Third Baseman", 71, 200, 35.07 ``` 该 CSV 文件在逗号(`,`)后包含空格。 要正确读取此 CSV 文件,请将`skipinitialspace`设置为`True`,如下所示: ```py import csv with open('baseball_players.csv', 'rt') as f: csv_reader = csv.reader(f, skipinitialspace=True) for line in csv_reader: print(line) ``` **预期输出**: ```py ['Name', 'Team', 'Position', 'Height(inches)', 'Weight(lbs)', 'Age'] ['Adam Donachie', 'BAL', 'Catcher', '74', '180', '22.99'] ['Paul Bako', 'BAL', 'Catcher', '74', '215', '34.69'] ['Ramon Hernandez', 'BAL', 'Catcher', '72', '210', '30.78'] ['Kevin Millar', 'BAL', 'First Baseman', '72', '210', '35.43'] ['Chris Gomez', 'BAL', 'First Baseman', '73', '188', '35.71'] ['Brian Roberts', 'BAL', 'Second Baseman', '69', '176', '29.39'] ['Miguel Tejada', 'BAL', 'Shortstop', '69', '209', '30.77'] ['Melvin Mora', 'BAL', 'Third Baseman', '71', '200', '35.07'] ``` ### `quotechar`参数 * * * **`address.csv`** ```py Name, Age, Address Jerry, 44, '2776 McDowell Street, Nashville, Tennessee' Tom, 21, '3171 Jessie Street, Westerville, Ohio' Mike, 32, '1818 Sherman Street, Hope, Kansas' ``` 此文件中有两件事需要注意。 首先,使用单引号(`'`)而不是`"`双引号(这是默认值)包装地址字段。 其次,逗号(`,`)后面有空格。 如果尝试在不更改引号的情况下读取此文件,则将获得以下输出: ```py import csv with open('addresses.csv', 'rt') as f: csv_reader = csv.reader(f, skipinitialspace=True) for line in csv_reader: print(line) ``` **预期输出**: ```py ['Name', 'Age', 'Address'] ['Jerry', '44', "'2776 McDowell Street", 'Nashville', "Tennessee'"] ['Tom', '21', "'3171 Jessie Street", 'Westerville', "Ohio'"] ['Mike', '32', "'1818 Sherman Street", 'Hope', "Kansas'"] ``` 请注意,地址分为三个字段,这当然是不正确的。 要解决此问题,只需使用`quotechar`参数将引号字符更改为单引号(`'`): ```py import csv with open('housing.csv', 'rt') as f: csv_reader = csv.reader(f, skipinitialspace=True, quotechar="'") for line in csv_reader: print(line) ``` **预期输出**: ```py ['Name', 'Age', 'Address'] ['Jerry', '44', '2776 McDowell Street, Nashville, Tennessee'] ['Tom', '21', '3171 Jessie Street, Westerville, Ohio'] ['Mike', '32', '1818 Sherman Street, Hope, Kansas'] ``` ### `escapechar`参数 * * * **`comments.csv`** ```py Id, User, Comment 1, Bob, "John said \"Hello World\"" 2, Tom, "\"The Magician\"" 3, Harry, "\"walk around the corner\" she explained to the child" 4, Louis, "He said, \"stop pulling the dog's tail\"" ``` 此文件使用反斜杠(`\`)字符转义嵌入的双引号。 但是,默认情况下,默认的`csv`模块使用双引号字符转义双引号字符。 如果尝试使用默认选项读取此文件,则将获得如下输出: ```py import csv with open('employees.csv', 'rt') as f: csv_reader = csv.reader(f, skipinitialspace=True) for line in csv_reader: print(line) ``` **预期输出**: ```py ['Id', 'User', 'Comment'] ['1', 'Bob', 'John said \\Hello World\\""'] ['2', 'Tom', '\\The Magician\\""'] ['3', 'Harry', '\\walk around the corner\\" she explained to the child"'] ['4', 'Louis', 'He said, \\stop pulling the dog\'s tail\\""'] ``` 这个输出肯定是不希望的。 要获得正确的输出,请使用`escapechar`参数更改转义符,如下所示: ```py import csv with open('employees.csv', 'rt') as f: csv_reader = csv.reader(f, skipinitialspace=True, escapechar='\\') for line in csv_reader: print(line) ``` **预期输出**: ```py ['Id', 'User', 'Comment'] ['1', 'Bob', 'John said "Hello World"'] ['2', 'Tom', '"The Magician"'] ['3', 'Harry', '"walk around the corner" she explained to the child'] ['4', 'Louis', 'He said, "stop pulling the dog\'s tail"'] ``` ### `doublequote`参数 * * * **`dialogs.csv`** ```py Id, Actor, Dialogue 1, Harley Betts, "The suspect told the arresting officer, ""I was nowhere near the crime.""" 2, Clyde Esparza, "John said, ""I have just finished reading Browning's 'My Last Duchess.'""" 3, Zack Campbell, "Bill asked Sandra, ""Will you marry me?""" 4, Keziah Chaney, "The librarian whispered to us, ""The sign on the wall says 'Quiet'""" ``` 此文件使用双引号转义字段中嵌入的双引号字符。 默认情况下,`doublequote`设置为`True`。 结果,在读取两个连续的双引号时会被解释为一个。 ```py import csv with open('employees.csv', 'rt') as f: # same as csv_reader = csv.reader(f, skipinitialspace=True) csv_reader = csv.reader(f, skipinitialspace=True, doublequote=True) for line in csv_reader: print(line) ``` **预期输出**: ```py ['Id', 'Actor', 'Dialogue'] ['1', 'Harley Betts', 'The suspect told the arresting officer, "I was nowhere near the crime."'] ['2', 'Clyde Esparza', 'John said, "I have just finished reading Browning\'s \'My Last Duchess.\'"'] ['3', 'Zack Campbell', 'Bill asked Sandra, "Will you marry me?"'] ['4', 'Keziah Chaney', 'The librarian whispered to us, "The sign on the wall says \'Quiet\'"'] ``` 但是,如果将`doublequote`设置为`False`,则连续的双引号将出现在输出中。 ```py import csv with open('employees.csv', 'rt') as f: csv_reader = csv.reader(f, skipinitialspace=True, doublequote=False) for line in csv_reader: print(line) ``` **预期输出**: ```py ['Id', 'Actor', 'Dialogue'] ['1', 'Harley Betts', 'The suspect told the arresting officer, "I was nowhere near the crime."""'] ['2', 'Clyde Esparza', 'John said, "I have just finished reading Browning\'s \'My Last Duchess.\'"""'] ['3', 'Zack Campbell', 'Bill asked Sandra, "Will you marry me?"""'] ['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)` | 写入多行数据并返回`None`。 `rows`必须是一个序列。 | 以下是示例: **示例 1** :使用`writerow()` ```py import csv header = ['id', 'name', 'address', 'zip'] rows = [ [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ], [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ], [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704], [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677], [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257], ] with open('customers.csv', 'wt') as f: csv_writer = csv.writer(f) csv_writer.writerow(header) # write header for row in rows: csv_writer.writerow(row) ``` **示例 2** :使用`writerows()` ```py import csv header = ['id', 'name', 'address', 'zip'] rows = [ [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ], [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ], [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704], [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677], [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257], ] with open('customers.csv', 'wt') as f: csv_writer = csv.writer(f) csv_writer.writerow(header) # write header csv_writer.writerows(rows) ``` 这两个清单生成的输出将是相同的,看起来像这样: **`customer.csv`** ```py id,name,address,zip 1,Hannah,"4891 Blackwell Street, Anchorage, Alaska",99503 2,Walton,"4223 Half and Half Drive, Lemoore, California",97401 3,Sam,"3952 Little Street, Akron, Ohio",93704 4,Chris,"3192 Flinderation Road, Arlington Heights, Illinois",62677 5,Doug,"3236 Walkers Ridge Way, Burr Ridge",61257 ``` 注意,只有地址字段用双引号引起来。 这是因为默认情况下`quoting`参数设置为`QUOTE_MINIMAL`。 换句话说,仅当`quotechar`或定界符出现在数据中时,字段才会被引用。 假设您要在所有文本数据中使用双引号。 为此,请将`quoting`参数设置为`QUOTE_NONNUMERIC`。 ```py import csv header = ['id', 'name', 'address', 'zip'] rows = [ [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ], [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ], [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704], [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677], [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257], ] with open('customers.csv', 'wt') as f: csv_writer = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC) csv_writer.writerow(header) # write header csv_writer.writerows(rows) ``` **预期输出**: **`customers.csv`** ```py "id","name","address","zip" 1,"Hannah","4891 Blackwell Street, Anchorage, Alaska",99503 2,"Walton","4223 Half and Half Drive, Lemoore, California",97401 3,"Sam","3952 Little Street, Akron, Ohio",93704 4,"Chris","3192 Flinderation Road, Arlington Heights, Illinois",62677 5,"Doug","3236 Walkers Ridge Way, Burr Ridge",61257 ``` 现在,所有名称和地址都用双引号引起来。 如果要在所有字段周围加双引号,而不管数据中是否出现`quotechar`或定界符,请将`quoting`设置为`csv.QUOTE_ALL`。 ```py import csv header = ['id', 'name', 'address', 'zip'] rows = [ [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ], [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ], [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704], [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677], [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257], ] with open('customers.csv', 'wt') as f: csv_writer = csv.writer(f, quoting=csv.QUOTE_ALL) csv_writer.writerow(header) # write header csv_writer.writerows(rows) ``` **预期输出**: ```py "id","name","address","zip" "1","Hannah","4891 Blackwell Street, Anchorage, Alaska","99503" "2","Walton","4223 Half and Half Drive, Lemoore, California","97401" "3","Sam","3952 Little Street, Akron, Ohio","93704" "4","Chris","3192 Flinderation Road, Arlington Heights, Illinois","62677" "5","Doug","3236 Walkers Ridge Way, Burr Ridge","61257" ``` 现在一切都被双引号了。 重要的是要注意,当引号打开时(即`quoting`参数的值不是`csv.QUOTE_NONE`),`csv`模块将使用`quotechar`(默认为`"`)对字段进行引号。 下面的清单将引号字符从双引号(`"`)更改为单引号(`'`)。 ```py import csv header = ['id', 'name', 'address', 'zip'] rows = [ [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ], [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ], [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704], [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677], [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257], ] with open('customers.csv', 'wt') as f: csv_writer = csv.writer(f, quotechar="'") csv_writer.writerow(header) # write header csv_writer.writerows(rows) ``` **预期输出**: ```py id,name,address,zip 1,Hannah,'4891 Blackwell Street, Anchorage, Alaska',99503 2,Walton,'4223 Half and Half Drive, Lemoore, California',97401 3,Sam,'3952 Little Street, Akron, Ohio',93704 4,Chris,'3192 Flinderation Road, Arlington Heights, Illinois',62677 5,Doug,'3236 Walkers Ridge Way, Burr Ridge',61257 ``` 在这种情况下,`csv`模块使用单引号(`'`)而不是(`"`)来引用包含 quotechar 或定界符的字段。 我们也可以通过将`quoting`设置为`csv.QUOTE_NONE`来关闭全部引用。 但是,如果这样做,并且分隔符出现在数据中,则将出现如下错误: ```py import csv header = ['id', 'name', 'address', 'zip'] rows = [ [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ], [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ], [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704], [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677], [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257], ] with open('customers.csv', 'wt') as f: csv_writer = csv.writer(f, quoting=csv.QUOTE_NONE) csv_writer.writerow(header) # write header csv_writer.writerows(rows) ``` **预期输出**: ```py Traceback (most recent call last): ... csv_writer.writerows(rows) _csv.Error: need to escape, but no escapechar set ``` 问题在于地址字段包含嵌入式逗号(`,`),并且由于我们已关闭了引用字段的功能,因此`csv`模块不知道如何正确对其进行转义。 这是`escapechar`参数起作用的地方。 它使用一个单字符字符串,当引号关闭时(即`quoting=csv.QUOTE_NONE`),该字符串将用于转义分隔符。 以下清单将`escapechar`设置为反斜杠(`\`)。 ```py import csv header = ['id', 'name', 'address', 'zip'] rows = [ [1, 'Hannah', '4891 Blackwell Street, Anchorage, Alaska', 99503 ], [2, 'Walton', '4223 Half and Half Drive, Lemoore, California', 97401 ], [3, 'Sam', '3952 Little Street, Akron, Ohio', 93704], [4, 'Chris', '3192 Flinderation Road, Arlington Heights, Illinois', 62677], [5, 'Doug', '3236 Walkers Ridge Way, Burr Ridge', 61257], ] with open('customers.csv', 'wt') as f: csv_writer = csv.writer(f, quoting=csv.QUOTE_NONE, escapechar='\\') csv_writer.writerow(header) # write header csv_writer.writerows(rows) ``` **预期输出**: ```py id,name,address,zip 1,Hannah,4891 Blackwell Street\, Anchorage\, Alaska,99503 2,Walton,4223 Half and Half Drive\, Lemoore\, California,97401 3,Sam,3952 Little Street\, Akron\, Ohio,93704 4,Chris,3192 Flinderation Road\, Arlington Heights\, Illinois,62677 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`** ```py id,name,address,zip 1,Hannah,4891 Blackwell Street\, Anchorage\, Alaska,99503 2,Walton,4223 Half and Half Drive\, Lemoore\, California,97401 3,Sam,3952 Little Street\, Akron\, Ohio,93704 4,Chris,3192 Flinderation Road\, Arlington Heights\, Illinois,62677 5,Doug,3236 Walkers Ridge Way\, Burr Ridge,61257 ``` ```py import csv with open('customers.csv', 'rt') as f: csv_reader = csv.DictReader(f, escapechar='\\') for row in csv_reader: print(row) ``` **预期输出**: ```py {'id': '1', 'name': 'Hannah', 'zip': '99503', 'address': '4891 Blackwell Street, Anchorage, Alaska'} {'id': '2', 'name': 'Walton', 'zip': '97401', 'address': '4223 Half and Half Drive, Lemoore, California'} {'id': '3', 'name': 'Sam', 'zip': '93704', 'address': '3952 Little Street, Akron, Ohio'} {'id': '4', 'name': 'Chris', 'zip': '62677', 'address': '3192 Flinderation Road, Arlington Heights, Illinois'} {'id': '5', 'name': 'Doug', 'zip': '61257', 'address': '3236 Walkers Ridge Way, Burr Ridge'} ``` **注意**:结果中键的顺序可能会有所不同。 由于字典不保留元素的顺序。 在这种情况下,字段名称是从 CSV 文件的第一行(或标题)推断出来的。 **示例 2** :使用`fieldnames`参数 ```py 1,Hannah,4891 Blackwell Street\, Anchorage\, Alaska,99503 2,Walton,4223 Half and Half Drive\, Lemoore\, California,97401 3,Sam,3952 Little Street\, Akron\, Ohio,93704 4,Chris,3192 Flinderation Road\, Arlington Heights\, Illinois,62677 5,Doug,3236 Walkers Ridge Way\, Burr Ridge,61257 ``` 此 CSV 文件没有标题。 因此,我们必须通过`fieldnames`参数提供字段名称。 ```py import csv with open('customers.csv', 'rt') as f: fields = ['id', 'name', 'address', 'zip'] csv_reader = csv.DictReader(f, fieldnames=fields, escapechar='\\') for row in csv_reader: print(row) ``` **预期输出**: ```py {'name': 'Hannah', 'zip': '99503', 'id': '1', 'address': '4891 Blackwell Street, Anchorage, Alaska'} {'name': 'Walton', 'zip': '97401', 'id': '2', 'address': '4223 Half and Half Drive, Lemoore, California'} {'name': 'Sam', 'zip': '93704', 'id': '3', 'address': '3952 Little Street, Akron, Ohio'} {'name': 'Chris', 'zip': '62677', 'id': '4', 'address': '3192 Flinderation Road, Arlington Heights, Illinois'} {'name': 'Doug', 'zip': '61257', 'id': '5', 'address': '3236 Walkers Ridge Way, Burr Ridge'} ``` **示例 3** :使用`restkey`参数 ```py 1,Hannah,4891 Blackwell Street\, Anchorage\, Alaska,99503 2,Walton,4223 Half and Half Drive\, Lemoore\, California,97401 3,Sam,3952 Little Street\, Akron\, Ohio,93704 4,Chris,3192 Flinderation Road\, Arlington Heights\, Illinois,62677 5,Doug,3236 Walkers Ridge Way\, Burr Ridge,61257 ``` ```py import csv with open('customers.csv', 'rt') as f: fields = ['id','name',] csv_reader = csv.DictReader(f, fieldnames=fields, restkey='extra', escapechar='\\') for row in csv_reader: print(row) ``` **预期输出**: ```py {'id': '1', 'name': 'Hannah', 'extra': ['4891 Blackwell Street, Anchorage, Alaska', '99503']} {'id': '2', 'name': 'Walton', 'extra': ['4223 Half and Half Drive, Lemoore, California', '97401']} {'id': '3', 'name': 'Sam', 'extra': ['3952 Little Street, Akron, Ohio', '93704']} {'id': '4', 'name': 'Chris', 'extra': ['3192 Flinderation Road, Arlington Heights, Illinois', '62677']} {'id': '5', 'name': 'Doug', 'extra': ['3236 Walkers Ridge Way, Burr Ridge', '61257']} ``` 请注意,地址和邮政编码现在存储为由值`extra`键控的序列。 **示例 4** :使用`restval` ```py 1,Hannah,4891 Blackwell Street\, Anchorage\, Alaska,99503 2,Walton,4223 Half and Half Drive\, Lemoore\, California,97401 3,Sam,3952 Little Street\, Akron\, Ohio,93704 4,Chris,3192 Flinderation Road\, Arlington Heights\, Illinois,62677 5,Doug,3236 Walkers Ridge Way\, Burr Ridge,61257 ``` ```py import csv with open('customers.csv', 'rt') as f: fields = ['id','name', 'address', 'zip', 'phone', 'email'] # two extra fields csv_reader = csv.DictReader(f, fieldnames=fields, restkey='extra', restval='NA', escapechar='\\') for row in csv_reader: print(row) ``` **预期输出**: ```py {'id': '1', 'name': 'Hannah', 'email': 'NA', 'phone': 'NA', 'address': '4891 Blackwell Street, Anchorage, Alaska', 'zip': '99503'} {'id': '2', 'name': 'Walton', 'email': 'NA', 'phone': 'NA', 'address': '4223 Half and Half Drive, Lemoore, California', 'zip': '97401'} {'id': '3', 'name': 'Sam', 'email': 'NA', 'phone': 'NA', 'address': '3952 Little Street, Akron, Ohio', 'zip': '93704'} {'id': '4', 'name': 'Chris', 'email': 'NA', 'phone': 'NA', 'address': '3192 Flinderation Road, Arlington Heights, Illinois', 'zip': '62677'} {'id': '5', 'name': 'Doug', 'email': 'NA', 'phone': 'NA', 'address': '3236 Walkers Ridge Way, Burr Ridge', 'zip': '61257'} ``` 在这种情况下,我们为字段指定了两个额外的字段:`phone`和`email`。 额外字段的值由`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)` | 写入多行数据并返回`None`。 `rows`必须是一个序列。 | Let's take some examples: **示例 1**: ```py import csv header = ['id', 'name', 'address', 'zip'] rows = [ {'id': 1, 'name': 'Hannah', 'address': '4891 Blackwell Street, Anchorage, Alaska', 'zip': 99503 }, {'id': 2, 'name': 'Walton', 'address': '4223 Half and Half Drive, Lemoore, California', 'zip': 97401 }, {'id': 3, 'name': 'Sam', 'address': '3952 Little Street, Akron, Ohio', 'zip': 93704 }, {'id': 4, 'name': 'Chris', 'address': '3192 Flinderation Road, Arlington Heights, Illinois', 'zip': 62677}, {'id': 5, 'name': 'Doug', 'address': '3236 Walkers Ridge Way, Burr Ridge', 'zip': 61257}, ] with open('dictcustomers.csv', 'wt') as f: csv_writer = csv.DictWriter(f, fieldnames=header) csv_writer.writeheader() # write header csv_writer.writerows(rows) ``` **预期输出**: **`dictcustomers.csv`** ```py id,name,address,zip 1,Hannah,"4891 Blackwell Street, Anchorage, Alaska",99503 2,Walton,"4223 Half and Half Drive, Lemoore, California",97401 3,Sam,"3952 Little Street, Akron, Ohio",93704 4,Chris,"3192 Flinderation Road, Arlington Heights, Illinois",62677 5,Doug,"3236 Walkers Ridge Way, Burr Ridge",61257 ``` **示例 2** :使用`restval` ```py import csv header = ['id', 'name', 'address', 'zip', 'email'] # an extra field email rows = [ {'id': 1, 'name': 'Hannah', 'address': '4891 Blackwell Street, Anchorage, Alaska', 'zip': 99503 }, {'id': 2, 'name': 'Walton', 'address': '4223 Half and Half Drive, Lemoore, California', 'zip': 97401 }, {'id': 3, 'name': 'Sam', 'address': '3952 Little Street, Akron, Ohio', 'zip': 93704 }, {'id': 4, 'name': 'Chris', 'address': '3192 Flinderation Road, Arlington Heights, Illinois', 'zip': 62677}, {'id': 5, 'name': 'Doug', 'address': '3236 Walkers Ridge Way, Burr Ridge', 'zip': 61257}, ] with open('dictcustomers.csv', 'wt') as f: csv_writer = csv.DictWriter(f, fieldnames=header, restval="NA") csv_writer.writeheader() # write header csv_writer.writerows(rows) ``` **预期输出**: **`dictcustomers.csv`** ```py id,name,address,zip,email 1,Hannah,"4891 Blackwell Street, Anchorage, Alaska",99503,NA 2,Walton,"4223 Half and Half Drive, Lemoore, California",97401,NA 3,Sam,"3952 Little Street, Akron, Ohio",93704,NA 4,Chris,"3192 Flinderation Road, Arlington Heights, Illinois",62677,NA 5,Doug,"3236 Walkers Ridge Way, Burr Ridge",61257,NA ``` 在这种情况下,字典中缺少`email`字段的值。 结果,`restval`的值将用于`email`字段。 **示例 3** :使用`extrasaction` ```py import csv header = ['id', 'name', 'address'] # notice zip is missing rows = [ {'id': 1, 'name': 'Hannah', 'address': '4891 Blackwell Street, Anchorage, Alaska', 'zip': 99503 }, {'id': 2, 'name': 'Walton', 'address': '4223 Half and Half Drive, Lemoore, California', 'zip': 97401 }, {'id': 3, 'name': 'Sam', 'address': '3952 Little Street, Akron, Ohio', 'zip': 93704 }, {'id': 4, 'name': 'Chris', 'address': '3192 Flinderation Road, Arlington Heights, Illinois', 'zip': 62677}, {'id': 5, 'name': 'Doug', 'address': '3236 Walkers Ridge Way, Burr Ridge', 'zip': 61257}, ] with open('dictcustomers.csv', 'wt') as f: csv_writer = csv.DictWriter( f, fieldnames=header, restval="NA", extrasaction='ignore' # ignore extra values in the dictionary ) csv_writer.writeheader() # write header csv_writer.writerows(rows) ``` 在此,词典包含一个名为`zip`的附加键,该键不在`header`列表中。 为了防止引发异常,我们将`extrasaction`设置为`ignore`。 **预期输出**: **`dictcustomers.csv`** ```py id,name,address 1,Hannah,"4891 Blackwell Street, Anchorage, Alaska" 2,Walton,"4223 Half and Half Drive, Lemoore, California" 3,Sam,"3952 Little Street, Akron, Ohio" 4,Chris,"3192 Flinderation Road, Arlington Heights, Illinois" 5,Doug,"3236 Walkers Ridge Way, Burr Ridge" ``` ## 创建方言 * * * 在本文的前面,我们学习了各种格式化参数,这些参数使我们可以自定义`reader`和`writer`对象,以适应 CSV 约定中的差异。 如果发现自己一次又一次地传递相同的格式参数集。 考虑创建自己的方言。 方言对象或(仅是方言)是对各种格式参数进行分组的一种方式。 一旦创建了方言对象,只需将其传递给阅读器或书写器,而不必分别传递每个格式参数。 要创建新的方言,我们使用`register_dialect()`函数。 它接受方言名称作为字符串,并接受一个或多个格式参数作为关键字参数。 下表列出了所有格式参数及其默认值: | 参数 | 默认值 | 描述 | | --- | --- | --- | | `delimiter` | `,` | 它是指用于分隔 CSV 文件中的值(或字段)的字符。 | | `skipinitialspace` | `False` | 它控制定界符后面的空格的解释方式。 如果为`True`,则将删除初始空格。 | | `lineterminator` | `\r\n` | 它是指用于终止行的字符序列。 | | `quotechar` | `"` | 它指的是如果字段中出现特殊字符(如定界符),则将用于引用值的单个字符串。 | | `quoting` | `csv.QUOTE_NONE` | 控制引号由作者生成或由读者识别的时间(其他选项请参见上文)。 | | `escapechar` | `None` | 引用设置为引号时,它用于转义定界符的一字符字符串。 | | `doublequote` | `True` | 控制字段内引号的处理。 当`True`时,在读取期间将两个连续的引号解释为一个,而在写入时,将嵌入数据中的每个引号字符写入为两个引号。 | 让我们创建一个简单的方言。 ```py import csv # create and register new dialect csv.register_dialect('psv', delimiter='|', quoting=csv.QUOTE_NONNUMERIC) header = ['id', 'year', 'age', 'name', 'movie'] rows = [ {'id': 1, 'year': 2013, 'age': 55, 'name': "Daniel Day-Lewis", 'movie': "Lincoln" }, {'id': 2, 'year': 2014, 'age': 44, 'name': "Matthew McConaughey", 'movie': "Dallas Buyers Club" }, {'id': 3, 'year': 2015, 'age': 33, 'name': "Eddie Redmayne", 'movie': "The Theory of Everything" }, {'id': 4, 'year': 2016, 'age': 41, 'name': "Leonardo DiCaprio", 'movie': "The Revenant" } ] with open('oscars.csv', 'wt') as f: csv_writer = csv.DictWriter( f, fieldnames=header, dialect='psv', # pass the new dialect extrasaction='ignore' ) csv_writer.writeheader() # write header csv_writer.writerows(rows) ``` **预期输出**: oscars.csv ```py "id"|"year"|"age"|"name"|"movie" 1|2013|55|"Daniel Day-Lewis"|"Lincoln" 2|2014|44|"Matthew McConaughey"|"Dallas Buyers Club" 3|2015|33|"Eddie Redmayne"|"The Theory of Everything" 4|2016|41|"Leonardo DiCaprio"|"The Revenant" ``` * * * * * *