Python csv.md
csv
csv模塊可以用於處理從電子表格和數據庫導出的數據到帶有字段和記錄格式的文本文件,通常稱為逗號分隔值(csv)格式,因為逗號通常用於分隔記錄中的字段。
Reading
csv.reader(csvfile, dialect=‘excel‘, **fmtparams):返回一個讀取器對象,它將在給定的csvfile中叠代。csvfile可以是任何支持iterator協議的對象,並且每次調用__next__()
方法時返回一個字符串 - file objects和列表對象都是合適的。如果csvfile是文件對象,則應使用newline=‘‘打開它。可以給出一個可選的方言參數,用於定義特定於某個CSV方言的一組參數。它可以是Dialect類的子類的實例或list_dialects()函數返回的字符串之一。可以給出其他可選的fmtparams關鍵字參數以覆蓋當前方言中的各個格式化參數。從csv文件讀取的每一行作為字符串列表返回。除非指定了QUOTE_NONNUMERIC格式選項(在這種情況下未引用的字段轉換為浮點型),否則不會執行自動數據類型轉換。
舉例
從csv文件中讀取數據, 可以使用reader()函數來創建一個讀取對象. 這個讀取對象順序處理文件的每一行, 可以把它當成叠代器使用, 例如:
import csv
import sys
with open(sys.argv[1], ‘rt‘) as f:
reader = csv.reader(f)
for row in reader:
print(row)
reader()的第一個參數指示源文本行, 在這個例子中, 是一個文件, 但它可以是任何可轉換的對象(StringIO對象, lists等). 指定其他可選的參數可用於控制輸入的數據如何被解析.
下面是測試的csv數據:
"Title 1","Title 2","Title 3","Title 4"
1,"a",08/18/07,"?"
2,"b",08/19/07,"∫"
3,"c",08/20/07,"?"
它被讀取時, 輸入數據的每一行被轉換為一個字符串列表.Linux輸出:
# python csv_study.py testdata.csv
[‘Title 1‘, ‘Title 2‘, ‘Title 3‘, ‘Title 4‘]
[‘1‘, ‘a‘, ‘08/18/07‘, ‘?‘]
[‘2‘, ‘b‘, ‘08/19/07‘, ‘∫‘]
[‘3‘, ‘c‘, ‘08/20/07‘, ‘?‘]
如果你知道特定的列具有特定的類型, 你就可以自行轉換, 但csv不會自動轉換. 它會自動處理嵌入在一行字符串中(這個行和輸入源文件的”行”意思是不同的)的換行符.
# cat testdata_1.csv
"Title 1","Title 2","Title 3","Title 4"
1,"first line
second line",08/18/07
Linux輸出:
# python csv_study.py testdata_1.csv
[‘Title 1‘, ‘Title 2‘, ‘Title 3‘, ‘Title 4‘]
[‘1‘, ‘first line\nsecond line‘, ‘08/18/07‘]
Writing
csv.writer(csvfile, dialect=‘excel‘, **fmtparams):返回一個writer對象,負責將用戶的數據轉換為給定類文件對象上的分隔字符串。csvfile可以是具有write()方法的任何對象。如果csvfile是文件對象,則應使用newline=‘ ‘ 打開。可以給出可選的方言參數,用於定義特定於特定CSV方言的一組參數。它可以是Dialect類的子類的實例或list_dialects()函數返回的字符串之一。可以給出其他可選的fmtparams關鍵字參數以覆蓋當前方言中的各個格式化參數。為了盡可能容易地與實現DB API的模塊接口,值None被寫為空字符串。雖然這不是可逆轉換,但它可以更輕松地將SQL NULL數據值轉儲到CSV文件,而無需預處理從cursor.fetch*調用返回的數據。所有其他非字符串數據在寫入之前用str()進行字符串化。
csvwriter.writerow(row):將行參數寫入寫入程序的文件對象,根據當前方言格式化。
csvwriter.writerows(rows):將所有行參數(如上所述的行對象的列表)寫入作者的文件對象,根據當前方言格式化。
csvwriter.dialect:作者使用的方言的只讀描述。
DictWriter.writeheader():用字段名寫入一行(在構造函數中指定)。
舉例
當你想把數據導入到其他應用程序中, 對CSV文件的寫入也是非常方便的. 使用writer()函數來創建一個寫入對象, 對於每一行, 使用writerow()來輸出一行.
import csv
import sys
unicode_chars = ‘?∫?‘
with open(sys.argv[1], ‘wt‘) as f:
writer = csv.writer(f)
writer.writerow((‘Title 1‘, ‘Title 2‘, ‘Title 3‘, ‘Title 4‘))
for i in range(3):
row = (i + 1, chr(ord(‘a‘) + i), ‘08/{:02d}/07‘.format(i + 1), unicode_chars[i])
writer.writerow(row)
print(open(sys.argv[1], ‘rt‘).read())
Linux 輸出:
# python csv_study.py testout.csv
Title 1,Title 2,Title 3,Title 4
1,a,08/01/07,?
2,b,08/02/07,∫
3,c,08/03/07,?
Quoting
還有4種不同的引用選項, 它們作為常量定義在csv模塊中.
QUOTE_ALL:不管是什麽類型, 任何內容都加上引號
QUOTE_MINIMAL:這是默認的, 使用指定的字符引用各個域(如果解析器被配置為相同的dialect和選項時, 可能會讓解析器在解析時產生混淆)
QUOTE_NONNUMERIC:引用那些不是整數或浮點數的域. 當使用讀取對象時, 如果輸入的域是沒有引號, 那麽它們會被轉換成浮點數.
QUOTE_NONE:對所有的輸出內容都不加引用, 當使用讀取對象時, 引用字符看作是包含在每個域的值裏(但在正常情況下, 他們被當成定界符而被去掉)
舉例
The default quoting behavior is different for the writer, so the second and third columns in the previous example are not quoted. To add quoting, set the quoting argument to one of the other quoting modes.
import sys
import csv
unicode_chars = ‘?∫?‘
with open(sys.argv[1], ‘wt‘) as f:
writer = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC)
writer.writerow((‘Title 1‘, ‘Title 2‘, ‘Title 3‘, ‘Title 4‘))
for i in range(3):
row = (i + 1, chr(ord(‘a‘) + i), ‘08/{:02d}/07‘.format(i + 1), unicode_chars[i])
writer.writerow(row)
print(open(sys.argv[1], ‘rt‘).read())
In this case, QUOTE_NONNUMERIC adds quotes around all columns that contain values that are not numbers.
輸出:
"Title 1","Title 2","Title 3","Title 4"
1,"a","08/01/07","?"
2,"b","08/02/07","∫"
3,"c","08/03/07","?"
Dialects
對於逗號分隔值文件,沒有定義良好的標準,因此解析器需要靈活。這種靈活性意味著有許多參數可以控制csv如何解析或寫入數據。它們不是將這些參數分別傳遞給閱讀器和寫入器,而是將它們組合成一個 dialect object.
Dialect classes可以通過名稱進行註冊,因此csv模塊的調用者不需要預先知道參數設置。可以用list_dialects()檢索完整的註冊方言列表。
import csv
print(csv.list_dialects())
輸出:
[‘unix‘, ‘excel‘, ‘excel-tab‘]
標準庫包括三種方言:excel、excel標簽和unix。excel dialect用於處理Microsoft excel的默認導出格式的數據,還可以與LibreOffice一起工作。unix dialect使用雙引號引用所有字段,並使用\n作為記錄分隔符。
Creating a Dialect
舉例
如果不使用逗號來限制字段,則輸入文件使用管道(|),如下所使用
"Title 1"|"Title 2"|"Title 3"
1|"first line
second line"|08/18/07
可以使用適當的分隔符來註冊一種新的dialect。
csv.register_dialect(‘pipes‘, delimiter=‘|‘)
with open(‘testdata.pipes‘, ‘r‘) as f:
reader = csv.reader(f, dialect=‘pipes‘)
for row in reader:
print(row)
輸出:
[‘Title 1‘, ‘Title 2‘, ‘Title 3‘]
[‘1‘, ‘first line\nsecond line‘, ‘08/18/07‘]
Dialect Parameters
一種dialect 指定解析或寫入數據文件時使用的所有標記。下面的表列出了可以指定的文件格式的各個方面,從分隔列的方式到用於轉義令牌的字符。
Attribute | Default | Meaning |
---|---|---|
delimiter | , | 用於分隔字段的單字符字符串。它默認為‘,‘。 |
doublequote | True | 控制在字段中出現的quotechar實例本身應如何引用。當True時,字符加倍。當False時,escapechar用作quotechar的前綴。默認為True。 |
escapechar | None | 寫入器將分隔符(如果引用)轉義的一個字符串設置為QUOTE_NONE和quotechar如果doublequote是False。讀取時,escapechar會刪除以下字符中的任何特殊含義。它默認為None,它禁用轉義。 |
lineterminator | \r\n | 用於終止由writer生成的行的字符串。它默認為‘\r\n‘。 |
quotechar | " | 用於引用包含特殊字符(例如分隔符或quotechar)或包含換行字符的字段的單字符字符串。它默認為‘"‘。 |
quoting | QUOTE_MINIMAL | 控制何時報價應由作者生成並由讀者識別。它可以接受任何QUOTE_*常量(參見Module Contents一節),默認為QUOTE_MINIMAL。 |
skipinitialspace | False | 當True時,緊跟分隔符後的空格將被忽略。默認值為False。 |
舉例
import sys
import csv
csv.register_dialect(‘escaped‘, escapechar=‘\\‘, doublequote=False, quoting=csv.QUOTE_NONE,)
csv.register_dialect(‘singlequote‘, quotechar="‘", quoting=csv.QUOTE_ALL)
quoting_modes = {getattr(csv, n): n for n in dir(csv) if n.startswith(‘QUOTE_‘)}
TEMPLATE = ‘‘‘Dialect: "{name}"
delimiter = {dl!r:<6} skipinitialspace = {si!r}
doublequote = {dq!r:<6} quoting = {qu}
quotechar = {qc!r:<6} lineterminator = {lt!r}
escapechar = {ec!r:<6}
‘‘‘
for name in sorted(csv.list_dialects()):
dialect = csv.get_dialect(name)
print(TEMPLATE.format(
name=name,
dl=dialect.delimiter,
si=dialect.skipinitialspace,
dq=dialect.doublequote,
qu=quoting_modes[dialect.quoting],
qc=dialect.quotechar,
lt=dialect.lineterminator,
ec=dialect.escapechar,
))
writer = csv.writer(sys.stdout, dialect=dialect)
writer.writerow(
(‘coll‘, 1, ‘10/01/2010‘,
‘Special chars: " \‘ {} to parse‘.format(dialect.delimiter))
)
print()
這個程序顯示了在使用不同的方言進行格式化時相同的數據是如何出現的。Linux輸出:
Dialect: "escaped"
delimiter = ‘,‘ skipinitialspace = 0
doublequote = 0 quoting = QUOTE_NONE
quotechar = ‘"‘ lineterminator = ‘\r\n‘
escapechar = ‘\\‘
coll,1,10/01/2010,Special chars: \" ‘ \, to parse
Dialect: "excel"
delimiter = ‘,‘ skipinitialspace = 0
doublequote = 1 quoting = QUOTE_MINIMAL
quotechar = ‘"‘ lineterminator = ‘\r\n‘
escapechar = None
coll,1,10/01/2010,"Special chars: "" ‘ , to parse"
Dialect: "excel-tab"
delimiter = ‘\t‘ skipinitialspace = 0
doublequote = 1 quoting = QUOTE_MINIMAL
quotechar = ‘"‘ lineterminator = ‘\r\n‘
escapechar = None
coll 1 10/01/2010 "Special chars: "" ‘ to parse"
Dialect: "singlequote"
delimiter = ‘,‘ skipinitialspace = 0
doublequote = 1 quoting = QUOTE_ALL
quotechar = "‘" lineterminator = ‘\r\n‘
escapechar = None
‘coll‘,‘1‘,‘10/01/2010‘,‘Special chars: " ‘‘ , to parse‘
Dialect: "unix"
delimiter = ‘,‘ skipinitialspace = 0
doublequote = 1 quoting = QUOTE_ALL
quotechar = ‘"‘ lineterminator = ‘\n‘
escapechar = None
"coll","1","10/01/2010","Special chars: "" ‘ , to parse"
Automatically Detecting Dialects
csv.register_dialect(name[, dialect[, **fmtparams]]):將方言與名稱相關聯。名稱必須是字符串。方言可以通過傳遞Dialect的子類或fmtparams關鍵字參數或兩者來指定,其中關鍵字參數覆蓋方言的參數。
csv.unregister_dialect(name):從方言註冊表中刪除與名稱關聯的方言。如果名稱不是註冊的方言名稱,則會引發Error。
csv.get_dialect(name):返回與名稱關聯的方言。如果名稱不是註冊的方言名稱,則會引發Error。此函數返回不可變的Dialect。
csv.list_dialects():返回所有註冊方言的名稱。
csv.field_size_limit([new_limit]):返回解析器允許的當前最大字段大小。如果給出new_limit,則這將成為新限制。
配置一個用於解析輸入文件的方言的最佳方法是預先知道正確的設置。對於方言參數未知的數據,Sniffer 類可以用來做一個有根據的猜測。sniff()方法接受輸入數據的一個示例,以及一個可選參數,提供可能的分隔符字符。
舉例
import csv
from io import StringIO
import textwrap
csv.register_dialect(‘escaped‘, escapechar=‘\\‘, doublequote=False, quoting=csv.QUOTE_NONE,)
csv.register_dialect(‘singlequote‘, quotechar="‘", quoting=csv.QUOTE_ALL)
samples = []
for name in sorted(csv.list_dialects()):
buffer = StringIO()
dialect = csv.get_dialect(name)
writer = csv.writer(buffer, dialect=dialect)
writer.writerow(
(‘coll‘, 1, ‘10/01/2010‘,
‘Special chars: " \‘ {} to parse‘.format(dialect.delimiter))
)
samples.append((name, dialect, buffer.getvalue()))
sniffer = csv.Sniffer()
for name, expected, sample in samples:
print(‘Dialect: "{}"‘.format(name))
print(‘In: {}‘.format(sample.rstrip()))
dialect = sniffer.sniff(sample, delimiters=‘,\t‘)
reader = csv.reader(StringIO(sample), dialect=dialect)
print(‘Parsed:\n {}\n‘.format(
‘\n ‘.join(repr(r) for r in next(reader))))
sniff()返回一個帶有用於解析數據的設置的Dialect實例。結果並不總是完美的,如示例中“escaped”的方言所演示的那樣。Linux輸出:
Dialect: "escaped"
In: coll,1,10/01/2010,Special chars: \" ‘ \, to parse
Parsed:
‘coll‘
‘1‘
‘10/01/2010‘
‘Special chars: \\" \‘ \\‘
‘ to parse‘
Dialect: "excel"
In: coll,1,10/01/2010,"Special chars: "" ‘ , to parse"
Parsed:
‘coll‘
‘1‘
‘10/01/2010‘
‘Special chars: " \‘ , to parse‘
Dialect: "excel-tab"
In: coll 1 10/01/2010 "Special chars: "" ‘ to parse"
Parsed:
‘coll‘
‘1‘
‘10/01/2010‘
‘Special chars: " \‘ \t to parse‘
Dialect: "singlequote"
In: ‘coll‘,‘1‘,‘10/01/2010‘,‘Special chars: " ‘‘ , to parse‘
Parsed:
‘coll‘
‘1‘
‘10/01/2010‘
‘Special chars: " \‘ , to parse‘
Dialect: "unix"
In: "coll","1","10/01/2010","Special chars: "" ‘ , to parse"
Parsed:
‘coll‘
‘1‘
‘10/01/2010‘
‘Special chars: " \‘ , to parse‘
Using Field Names
class csv.DictReader(csvfile, fieldnames=None, restkey=None, restval=None, dialect=‘excel‘, *args, **kwds):創建一個對象,其操作類似於普通讀取器,但將讀取的信息映射到一個dict中,其中的鍵由可選的fieldnames參數給出。fieldnames參數是一個sequence,其元素按順序與輸入數據的字段相關聯。這些元素成為結果字典的鍵。如果省略fieldnames參數,則csvfile的第一行中的值將用作字段名稱。如果讀取的行具有比字段名序列更多的字段,則剩余數據將作為鍵值為restkey的序列添加。如果讀取的行具有比字段名序列少的字段,則剩余的鍵使用可選的restval參數的值。任何其他可選或關鍵字參數都傳遞給底層的reader實例。
class csv.DictWriter(csvfile, fieldnames, restval=‘‘, extrasaction=‘raise‘, dialect=‘excel‘, *args, **kwds)
創建一個操作類似於常規writer的對象,但將字典映射到輸出行。fieldnames參數是一個sequence,用於標識傳遞給writerow()方法的字典中的值被寫入csvfile。如果字典在fieldnames中缺少鍵,則可選的restval參數指定要寫入的值。如果傳遞給writerow()方法的字典包含fieldnames中未找到的鍵,則可選的extrasaction參數指示要執行的操作。如果設置為‘raise‘,則會引發ValueError。如果設置為‘ignore‘,則會忽略字典中的額外值。任何其他可選或關鍵字參數都傳遞給底層的writer實例。請註意,與DictReader類不同,DictWriter的fieldnames參數不是可選的。由於Python的dict對象沒有排序,因此沒有足夠的信息來推斷將該行寫入到csvfile的順序。
舉例
另外, 在處理數據序列時, csv模塊包含了一些將行作為字典進行處理的類. 類DictReader和類DictWriter將每一行轉成字典對象, 可以傳遞字典鍵值, 或者從輸入文件的第一行中推斷出鍵值.
import sys
import csv
with open(sys.argv[1], ‘rt‘) as f:
reader = csv.DictReader(f)
for row in reader:
print(row)
基於字典的讀取和寫入對象可以當作是基於序列對象的進一步實現, 它們使用相同的參數和API. 唯一的差別就是前者把每一行當成是字典而不是列表或元組.Linux輸出:
# python csv_study.py testdata.csv
{‘Title 2‘: ‘first line\nsecond line‘, ‘Title 1‘: ‘1‘, ‘Title 3‘: ‘08/18/07‘, ‘Title 4‘: None}
{‘Title 2‘: ‘a‘, ‘Title 1‘: ‘1‘, ‘Title 3‘: ‘08/18/07‘, ‘Title 4‘: ‘?‘}
{‘Title 2‘: ‘b‘, ‘Title 1‘: ‘2‘, ‘Title 3‘: ‘08/19/07‘, ‘Title 4‘: ‘∫‘}
{‘Title 2‘: ‘c‘, ‘Title 1‘: ‘3‘, ‘Title 3‘: ‘08/20/07‘, ‘Title 4‘: ‘?‘}
DictWriter必須指定一個域名字的列表, 因為這樣它才在輸出時知道每個列的順序.
import csv
import sys
fieldnames = (‘Title 1‘, ‘Title 2‘, ‘Title 3‘, ‘Title 4‘)
headers = {
n: n
for n in fieldnames
}
unicode_chars = ‘?∫?‘
with open(sys.argv[1], ‘wt‘) as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for i in range(3):
writer.writerow({
‘Title 1‘: i + 1,
‘Title 2‘: chr(ord(‘a‘) + i),
‘Title 3‘: ‘08/{:02d}/07‘.format(i + 1),
‘Title 4‘: unicode_chars[i],
})
print(open(sys.argv[1], ‘rt‘).read())
輸出:
Title 1,Title 2,Title 3,Title 4
1,a,08/01/07,?
2,b,08/02/07,∫
3,c,08/03/07,?
Python csv.md