# 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"
```
* * *
* * *
- 初級 Python
- python 入門
- 安裝 Python3
- 運行 python 程序
- 數據類型和變量
- Python 數字
- Python 字符串
- Python 列表
- Python 字典
- Python 元組
- 數據類型轉換
- Python 控制語句
- Python 函數
- Python 循環
- Python 數學函數
- Python 生成隨機數
- Python 文件處理
- Python 對象和類
- Python 運算符重載
- Python 繼承與多態
- Python 異常處理
- Python 模塊
- 高級 Python
- Python *args和**kwargs
- Python 生成器
- Python 正則表達式
- 使用 PIP 在 python 中安裝包
- Python virtualenv指南
- Python 遞歸函數
- __name__ == "__main__"是什么?
- Python Lambda 函數
- Python 字符串格式化
- Python 內置函數和方法
- Python abs()函數
- Python bin()函數
- Python id()函數
- Python map()函數
- Python zip()函數
- Python filter()函數
- Python reduce()函數
- Python sorted()函數
- Python enumerate()函數
- Python reversed()函數
- Python range()函數
- Python sum()函數
- Python max()函數
- Python min()函數
- Python eval()函數
- Python len()函數
- Python ord()函數
- Python chr()函數
- Python any()函數
- Python all()函數
- Python globals()函數
- Python locals()函數
- 數據庫訪問
- 安裝 Python MySQLdb
- 連接到數據庫
- MySQLdb 獲取結果
- 插入行
- 處理錯誤
- 使用fetchone()和fetchmany()獲取記錄
- 常見做法
- Python:如何讀取和寫入文件
- Python:如何讀取和寫入 CSV 文件
- 用 Python 讀寫 JSON
- 用 Python 轉儲對象