csv --- CSV 檔案讀寫

原始碼: Lib/csv.py


所謂的 CSV (逗號分隔值) 格式是電子表格和資料庫最常用的匯入和匯出格式。在 RFC 4180 嘗試以標準方式描述該格式之前的許多年裡,CSV 格式就一直在使用。由於缺少明確的標準,不同應用程式生成和使用的資料通常存在細微差異。這些差異使得處理來自多個來源的 CSV 檔案變得惱人。儘管如此,雖然分隔符和引號字元各不相同,但整體格式足夠相似,因此可以編寫一個模組來高效地處理此類資料,從而向程式設計師隱藏讀寫資料的細節。

csv 模組實現了用於讀寫 CSV 格式的表格資料的類。它允許程式設計師說,“以 Excel 首選的格式寫入此資料”,或者“從此 Excel 生成的檔案中讀取資料”,而無需瞭解 Excel 使用的 CSV 格式的精確細節。程式設計師還可以描述其他應用程式理解的 CSV 格式,或定義自己的專用 CSV 格式。

csv 模組的 readerwriter 物件可以讀寫序列。程式設計師還可以使用 DictReaderDictWriter 類以字典形式讀寫資料。

參見

PEP 305 - CSV 檔案 API

提議將此功能新增到 Python 的 Python 增強提案。

模組內容

csv 模組定義了以下函式

csv.reader(csvfile, /, dialect='excel', **fmtparams)

返回一個 reader 物件,該物件將處理給定 csvfile 中的行。csvfile 必須是字串的可迭代物件,每一行都採用 reader 定義的 csv 格式。csvfile 通常是類檔案物件或列表。如果 csvfile 是一個檔案物件,則應使用 newline='' 開啟。[1] 可以提供一個可選的 dialect 引數,用於定義特定 CSV 方言的一組引數。它可以是 Dialect 類的子類的例項,也可以是 list_dialects() 函式返回的字串之一。其他可選的 fmtparams 關鍵字引數可用於覆蓋當前方言中的個別格式化引數。有關方言和格式化引數的完整詳細資訊,請參見 方言和格式化引數 部分。

從 csv 檔案中讀取的每一行都作為字串列表返回。除非指定了 QUOTE_NONNUMERIC 格式選項(在這種情況下,未加引號的欄位將轉換為浮點數),否則不會執行自動資料型別轉換。

一個簡短的使用示例

>>> import csv
>>> with open('eggs.csv', newline='') as csvfile:
...     spamreader = csv.reader(csvfile, delimiter=' ', quotechar='|')
...     for row in spamreader:
...         print(', '.join(row))
Spam, Spam, Spam, Spam, Spam, Baked Beans
Spam, Lovely Spam, Wonderful Spam
csv.writer(csvfile, /, dialect='excel', **fmtparams)

返回一個 writer 物件,負責將使用者的資料轉換為給定類檔案物件上的分隔字串。csvfile 可以是任何具有 write() 方法的物件。如果 csvfile 是檔案物件,則應使用 newline='' 開啟 [1]。可以提供一個可選的 dialect 引數,用於定義特定 CSV 方言的一組引數。它可以是 Dialect 類的子類的例項,也可以是 list_dialects() 函式返回的字串之一。其他可選的 fmtparams 關鍵字引數可用於覆蓋當前方言中的個別格式化引數。有關方言和格式化引數的完整詳細資訊,請參見 方言和格式化引數 部分。為了儘可能方便地與實現 DB API 的模組對接,值 None 會被寫為空字串。雖然這不是一個可逆的轉換,但它使得將 SQL NULL 資料值轉儲到 CSV 檔案變得更容易,而無需預處理從 cursor.fetch* 呼叫返回的資料。所有其他非字串資料在寫入前都會用 str() 進行字串化。

一個簡短的使用示例

import csv
with open('eggs.csv', 'w', newline='') as csvfile:
    spamwriter = csv.writer(csvfile, delimiter=' ',
                            quotechar='|', quoting=csv.QUOTE_MINIMAL)
    spamwriter.writerow(['Spam'] * 5 + ['Baked Beans'])
    spamwriter.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])
csv.register_dialect(name, /, dialect='excel', **fmtparams)

dialectname 關聯。name 必須是字串。方言可以透過傳遞 Dialect 的子類,或透過 fmtparams 關鍵字引數,或兩者兼有來指定,其中關鍵字引數會覆蓋方言的引數。有關方言和格式化引數的完整詳細資訊,請參見 方言和格式化引數 部分。

csv.unregister_dialect(name)

從方言登錄檔中刪除與 name 關聯的方言。如果 name 不是已註冊的方言名稱,則引發 Error

csv.get_dialect(name)

返回與 name 關聯的方言。如果 name 不是已註冊的方言名稱,則引發 Error。此函式返回一個不可變的 Dialect

csv.list_dialects()

返回所有已註冊方言的名稱。

csv.field_size_limit()
csv.field_size_limit(new_limit)

返回解析器當前允許的最大欄位大小。如果給定了 new_limit,它將成為新的限制。

csv 模組定義了以下類

class csv.DictReader(f, fieldnames=None, restkey=None, restval=None, dialect='excel', *args, **kwds)

建立一個類似於常規 reader 的物件,但將每行中的資訊對映到一個 dict,其鍵由可選的 fieldnames 引數給出。

fieldnames 引數是一個序列。如果省略 fieldnames,檔案 f 第一行中的值將用作欄位名,並且這些值將從結果中省略。如果提供了 fieldnames,它們將被使用,並且第一行將包含在結果中。無論欄位名是如何確定的,字典都會保留其原始順序。

如果一行的欄位多於欄位名,則剩餘的資料會放入一個列表中,並以 restkey(預設為 None)指定的欄位名儲存。如果一個非空行的欄位少於欄位名,則缺失的值將用 restval 的值(預設為 None)填充。

所有其他可選引數或關鍵字引數都將傳遞給底層的 reader 例項。

如果傳遞給 fieldnames 的引數是迭代器,它將被強制轉換為 list

在 3.6 版本發生變更: 返回的行現在是 OrderedDict 型別。

在 3.8 版本發生變更: 返回的行現在是 dict 型別。

一個簡短的使用示例

>>> import csv
>>> with open('names.csv', newline='') as csvfile:
...     reader = csv.DictReader(csvfile)
...     for row in reader:
...         print(row['first_name'], row['last_name'])
...
Eric Idle
John Cleese

>>> print(row)
{'first_name': 'John', 'last_name': 'Cleese'}
class csv.DictWriter(f, fieldnames, restval='', extrasaction='raise', dialect='excel', *args, **kwds)

建立一個類似於常規 writer 的物件,但將字典對映到輸出行。fieldnames 引數是一個鍵的序列,用於標識傳遞給 writerow() 方法的字典中的值寫入檔案 f 的順序。可選的 restval 引數指定當字典缺少 fieldnames 中的某個鍵時要寫入的值。如果傳遞給 writerow() 方法的字典包含在 fieldnames 中找不到的鍵,可選的 extrasaction 引數指示要採取的操作。如果設定為 'raise'(預設值),則會引發 ValueError。如果設定為 'ignore',則字典中的額外值將被忽略。任何其他可選引數或關鍵字引數都將傳遞給底層的 writer 例項。

請注意,與 DictReader 類不同,DictWriter 類的 fieldnames 引數不是可選的。

如果傳遞給 fieldnames 的引數是迭代器,它將被強制轉換為 list

一個簡短的使用示例

import csv

with open('names.csv', 'w', newline='') as csvfile:
    fieldnames = ['first_name', 'last_name']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

    writer.writeheader()
    writer.writerow({'first_name': 'Baked', 'last_name': 'Beans'})
    writer.writerow({'first_name': 'Lovely', 'last_name': 'Spam'})
    writer.writerow({'first_name': 'Wonderful', 'last_name': 'Spam'})
class csv.Dialect

Dialect 類是一個容器類,其屬性包含有關如何處理雙引號、空格、分隔符等的資訊。由於缺乏嚴格的 CSV 規範,不同的應用程式會產生略有不同的 CSV 資料。Dialect 例項定義了 readerwriter 例項的行為方式。

所有可用的 Dialect 名稱都由 list_dialects() 返回,並且可以透過其初始化程式(__init__)函式向特定的 readerwriter 類註冊,如下所示

import csv

with open('students.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile, dialect='unix')
class csv.excel

excel 類定義了 Excel 生成的 CSV 檔案的通常屬性。它以方言名稱 'excel'> 註冊。

class csv.excel_tab

excel_tab 類定義了 Excel 生成的 TAB 分隔檔案的通常屬性。它以方言名稱 'excel-tab'> 註冊。

class csv.unix_dialect

unix_dialect 類定義了在 UNIX 系統上生成的 CSV 檔案的通常屬性,即使用 '\n' 作為行終止符並引用所有欄位。它以方言名稱 'unix'> 註冊。

在 3.2 版本加入。

class csv.Sniffer

Sniffer 類用於推斷 CSV 檔案的格式。

Sniffer 類提供了兩種方法

sniff(sample, delimiters=None)

分析給定的 sample 並返回一個反映所找到引數的 Dialect 子類。如果給定了可選的 delimiters 引數,則將其解釋為包含可能的有效分隔符字元的字串。

has_header(sample)

分析示例文字(假定為 CSV 格式),如果第一行看起來是一系列列標題,則返回 True。透過檢查每一列,將考慮以下兩個關鍵標準之一來評估樣本是否包含標題

  • 第二行到第 n 行包含數值

  • 第二行到第 n 行包含字串,其中至少有一個值的長度與該列的假定標題的長度不同。

對第一行之後的二十行進行抽樣;如果超過一半的列+行符合標準,則返回 True

備註

此方法是一種粗略的啟發式方法,可能會產生假陽性和假陰性。

Sniffer 的使用示例

with open('example.csv', newline='') as csvfile:
    dialect = csv.Sniffer().sniff(csvfile.read(1024))
    csvfile.seek(0)
    reader = csv.reader(csvfile, dialect)
    # ... process CSV file contents here ...

csv 模組定義了以下常量

csv.QUOTE_ALL

指示 writer 物件引用所有欄位。

csv.QUOTE_MINIMAL

指示 writer 物件僅引用那些包含特殊字元(如 delimiterquotechar'\r''\n'lineterminator 中的任何字元)的欄位。

csv.QUOTE_NONNUMERIC

指示 writer 物件引用所有非數字欄位。

指示 reader 物件將所有未加引號的欄位轉換為 float 型別。

備註

某些數值型別,如 boolFractionIntEnum,其字串表示無法轉換為 float。它們無法在 QUOTE_NONNUMERICQUOTE_STRINGS 模式下讀取。

csv.QUOTE_NONE

指示 writer 物件從不引用欄位。噹噹前的 delimiterquotecharescapechar'\r''\n'lineterminator 中的任何字元出現在輸出資料中時,它前面會加上當前的 escapechar 字元。如果未設定 escapechar,則在遇到任何需要轉義的字元時,writer 將引發 Error。將 quotechar 設定為 None> 以防止其轉義。

指示 reader 物件不對引號字元執行特殊處理。

csv.QUOTE_NOTNULL

指示 writer 物件引用所有非 None 的欄位。這類似於 QUOTE_ALL,但如果欄位值為 None>,則會寫入一個空的(未加引號的)字串。

指示 reader 物件將空的(未加引號的)欄位解釋為 None>,否則行為與 QUOTE_ALL 相同。

3.12 新版功能.

csv.QUOTE_STRINGS

指示 writer 物件始終在字串欄位周圍加上引號。這類似於 QUOTE_NONNUMERIC,但如果欄位值為 None>,則會寫入一個空的(未加引號的)字串。

指示 reader 物件將空的(未加引號的)字串解釋為 None>,否則行為與 QUOTE_NONNUMERIC 相同。

3.12 新版功能.

csv 模組定義了以下異常

exception csv.Error

在檢測到錯誤時由任何函式引發。

方言和格式化引數

為了更容易地指定輸入和輸出記錄的格式,特定的格式化引數被組合成方言。方言是 Dialect 類的子類,包含描述 CSV 檔案格式的各種屬性。建立 readerwriter 物件時,程式設計師可以指定一個字串或 Dialect 類的子類作為方言引數。除了或代替 dialect 引數,程式設計師還可以指定個別格式化引數,這些引數與下面為 Dialect 類定義的屬性同名。

方言支援以下屬性

Dialect.delimiter

用於分隔欄位的單字元字串。預設為 ','

Dialect.doublequote

控制出現在欄位內的 quotechar 例項應如何被引用。當為 True 時,該字元會被加倍。當為 False 時,escapechar 將用作 quotechar 的字首。預設為 True

輸出時,如果 doublequoteFalse 且未設定 escapechar,則在欄位中找到 quotechar 時會引發 Error

Dialect.escapechar

writer 用於轉義需要轉義的字元的單字元字串

  • 如果 quoting 設定為 QUOTE_NONE,則 delimiterquotechar'\r''\n'lineterminator 中的任何字元都會被轉義;

  • 如果 doublequoteFalse,則 quotechar 會被轉義;

  • escapechar 本身。

讀取時,escapechar 會移除其後字元的任何特殊含義。它預設為 None,表示停用轉義。

在 3.11 版本發生變更: 不允許空的 escapechar

Dialect.lineterminator

用於終止 writer 生成的行的字串。預設為 '\r\n'

備註

reader 硬編碼為識別 '\r'> 或 '\n' 作為行尾,並忽略 lineterminator。此行為將來可能會改變。

Dialect.quotechar

用於引用包含特殊字元(如 delimiterquotechar)或包含換行符('\r''\n'lineterminator 中的任何字元)的欄位的單字元字串。預設為 '"'。如果 quoting 設定為 QUOTE_NONE,可以設定為 None> 以防止轉義 '"'

在 3.11 版本發生變更: 不允許空的 quotechar

Dialect.quoting

控制 writer 何時生成引號以及 reader 何時識別引號。它可以是任何 QUOTE_* 常量,如果 quotechar 不是 None,則預設為 QUOTE_MINIMAL,否則預設為 QUOTE_NONE

Dialect.skipinitialspace

當為 True 時,緊跟在 delimiter 之後的空格將被忽略。預設為 False。當 delimiter=' '> 與 skipinitialspace=True> 結合使用時,不允許未加引號的空欄位。

Dialect.strict

當為 True 時,在遇到錯誤的 CSV 輸入時引發 Error 異常。預設為 False

Reader 物件

Reader 物件(DictReader 例項和由 reader() 函式返回的物件)具有以下公共方法

csvreader.__next__()

返回 reader 的可迭代物件的下一行,格式為列表(如果物件是從 reader() 返回的)或字典(如果它是 DictReader 例項),並根據當前的 Dialect 進行解析。通常你應該以 next(reader)> 的形式呼叫它。

Reader 物件具有以下公共屬性

csvreader.dialect

解析器使用的方言的只讀描述。

csvreader.line_num

從源迭代器讀取的行數。這與返回的記錄數不同,因為記錄可以跨越多行。

DictReader 物件具有以下公共屬性

DictReader.fieldnames

如果在建立物件時未作為引數傳遞,此屬性會在首次訪問或從檔案中讀取第一條記錄時初始化。

Writer 物件

writer 物件(DictWriter 例項和由 writer() 函式返回的物件)具有以下公共方法。對於 writer 物件,row 必須是字串或數字的可迭代物件;對於 DictWriter 物件,row 必須是將欄位名對映到字串或數字的字典(透過先傳遞給 str())。請注意,複數會用括號括起來寫入。這可能會給其他讀取 CSV 檔案的程式帶來一些問題(假設它們支援複數)。

csvwriter.writerow(row, /)

row 引數寫入 writer 的檔案物件,並根據當前的 Dialect 進行格式化。返回底層檔案物件的 write 方法呼叫的返回值。

在 3.5 版本發生變更: 增加了對任意可迭代物件的支援。

csvwriter.writerows(rows, /)

rows(一個如上所述的 row 物件的可迭代物件)中的所有元素寫入 writer 的檔案物件,並根據當前方言進行格式化。

Writer 物件具有以下公共屬性

csvwriter.dialect

writer 使用的方言的只讀描述。

DictWriter 物件具有以下公共方法

DictWriter.writeheader()

將一行帶有欄位名(在建構函式中指定)的資料寫入 writer 的檔案物件,並根據當前方言進行格式化。返回內部使用的 csvwriter.writerow() 呼叫的返回值。

在 3.2 版本加入。

在 3.8 版本發生變更: writeheader() 現在也返回其內部使用的 csvwriter.writerow() 方法返回的值。

示例

讀取 CSV 檔案的最簡單示例

import csv
with open('some.csv', newline='') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

讀取備用格式的檔案

import csv
with open('passwd', newline='') as f:
    reader = csv.reader(f, delimiter=':', quoting=csv.QUOTE_NONE)
    for row in reader:
        print(row)

相應最簡單的寫入示例是

import csv
with open('some.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerows(someiterable)

由於 open() 用於開啟 CSV 檔案進行讀取,因此預設情況下,檔案將使用系統預設編碼解碼為 unicode(參見 locale.getencoding())。要使用不同的編碼解碼檔案,請使用 open 的 encoding> 引數

import csv
with open('some.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

這同樣適用於以非系統預設編碼寫入:在開啟輸出檔案時指定 encoding 引數。

註冊一個新方言

import csv
csv.register_dialect('unixpwd', delimiter=':', quoting=csv.QUOTE_NONE)
with open('passwd', newline='') as f:
    reader = csv.reader(f, 'unixpwd')

一個更高階的 reader 用法——捕獲和報告錯誤

import csv, sys
filename = 'some.csv'
with open(filename, newline='') as f:
    reader = csv.reader(f)
    try:
        for row in reader:
            print(row)
    except csv.Error as e:
        sys.exit(f'file {filename}, line {reader.line_num}: {e}')

雖然該模組不直接支援解析字串,但可以很容易地實現

import csv
for row in csv.reader(['one,two,three']):
    print(row)

腳註