讀取文件不再使用 With Open
有時我們需要把數(shù)據(jù)永久存儲起來,隨時使用隨時讀取。例如,我們通過程序建立的列表、字典等數(shù)據(jù),當(dāng)程序結(jié)束時,需要把這些數(shù)據(jù)存儲到文件中,當(dāng)程序再次啟動時,可以把這些數(shù)據(jù)讀入到程序中,避免這些數(shù)據(jù)的重新錄入。
在Python語言中,負(fù)責(zé)文件操作的稱為文件對象,文件對象不僅可以訪問存儲在磁盤中的文件,也可以訪問網(wǎng)絡(luò)文件。文件對象通過open函數(shù)得到,獲取文件對象后,就可以使用文件對象提供的方法來讀寫文件。
但open函數(shù)在處理某些問題是并不是很理想,有沒有其他比open函數(shù)更加適合讀取某些特定文件呢?下面我們就一起來看看!
回顧open函數(shù)
對文件操作使用最頻繁對函數(shù),open()打開一個文件對象,使用Python內(nèi)置的open()函數(shù),傳入文件名和模式。
- name: 要讀取的文件名稱。
- mode?: 打開文件的模式,選填。r, r+, w, w+, a, a+使用最多。
- buffering: 文件所需的緩沖區(qū)大小, 選填。0表示無緩沖, 1表示線路緩沖。有四種打開文件的不同方法(模式)
- "r" - 讀取 - 默認(rèn)值。打開文件進(jìn)行讀取,如果文件不存在則報錯。
- "a" - 追加 - 打開供追加的文件,如果不存在則創(chuàng)建該文件。
- "w" - 寫入 - 打開文件進(jìn)行寫入,如果文件不存在則創(chuàng)建該文件。
- "x" - 創(chuàng)建 - 創(chuàng)建指定的文件,如果文件存在則返回錯誤。
- 此外,可以指定文件是應(yīng)該作為二進(jìn)制還是文本模式進(jìn)行處理。
- "t" - 文本 - 默認(rèn)值。文本模式。
- "b" - 二進(jìn)制 - 二進(jìn)制模式(例如圖像)。
使用時常通過with open()方法讀寫文件。
pathlib
以前在Python中操作文件路徑,更多的時候是使用os模塊。Python3的系統(tǒng)標(biāo)準(zhǔn)庫pathlib模塊的Path對路徑的操作會更簡單。
pathlib的一些基本操作,如文件名操作、路徑操作、文件操作等等并不在本文討論范圍。本此介紹使用率非常高的文件操作,其文件讀寫方法。
Path.open
在pathlib里如果要打開一個文件十分的簡單。
打開路徑指向的文件,就像內(nèi)置的open()函數(shù)所做的一樣。
結(jié)果
Path讀與寫
對于簡單的文件讀寫,在pathlib模塊中有幾個簡便的方法:
- Path.read_text(): 以字符串形式返回路徑指向的文件的解碼后文本內(nèi)容。
- Path.read_bytes(): 以二進(jìn)制/字節(jié)模式打開路徑并以字節(jié)串的形式返回內(nèi)容。
- Path.write_text(): 打開路徑并向其寫入字符串?dāng)?shù)據(jù)。
- Path.write_bytes(): 以二進(jìn)制/字節(jié)模式打開路徑并向其寫入數(shù)據(jù)。
更多詳情可參見pathlib模塊[1]。
fileinput
如果你只想讀取一個文件,使用open()。如果需要實現(xiàn)文件列表的批量循環(huán)操作,不妨使用本模塊。
fileinput.input
input是fileinput模塊的初始接口,其使用也是較簡單。
- files 需要讀取的文件對象,可迭代對象。
- inplace 標(biāo)準(zhǔn)輸出重定向替換,表示是否將標(biāo)準(zhǔn)輸出的結(jié)果寫回文件,默認(rèn)不取代。
- backup? 讀取時同時備份文件,可以指定備份的后綴名,比如 backup='.bak'。
- mode? 文件讀取模式,fileinput 有且僅有這兩種讀取模式r?和 rb。
- 默認(rèn)使用mode='r'
- 如果文件是二進(jìn)制的,可以使用mode='rb' 模式。
- openhook 支持用戶傳入自定義的對象讀取方法。fileinput 內(nèi)置了兩個勾子函數(shù):
- fileinput.hook_encoded(encoding, errors=None)使用gzip? 和bz2模塊透明地打開 gzip 和 bzip2 壓縮的文件
- fileinput.hook_compressed(filename, mode)使用給定的 encoding 和 errors 來讀取文件。
從標(biāo)準(zhǔn)輸入中讀取
若input()不傳任何參數(shù)時,fileinput 默認(rèn)會以 stdin 作為輸入源。
運行stdinput.py后,在編譯器中輸入內(nèi)容,程序會自動讀取并再打印一次。
從指定文件中讀取
讀取批量文件
輸出
由于 info.txt 和 info.txt 的內(nèi)容被整合成一個文件對象 file ,因此 fileinput.lineno() 只有在讀取一個文件時,才是原文件中真實的行號。如果想要在讀取多個文件的時候,也能讀取原文件的真實行號,可以使用 fileinput.filelineno() 方法。
以上幾個常用對方法解釋如下。
- fileinput.filename()返回當(dāng)前被讀取的文件名。在第一行被讀取之前,返回None。
- fileinput.lineno()返回已被讀取的累計行號。在第一行被讀取之前,返回 0。在最后一個文件的最后一行被讀取之后,返回該行的行號。
- fileinput.filelineno()返回當(dāng)前文件中的行號。在第一行被讀取之前,返回 0。在最后一個文件的最后一行被讀取之后,返回此文件中該行的行號。
讀取單個文件
與批量讀取文件一樣,只需要在參數(shù)files中傳人一個文件即可。
輸出
與glob配合批量讀取
glob簡介
glob是python自帶的一個操作文件的相關(guān)模塊,可以對文件夾下所有文件進(jìn)行遍歷,并將符合匹配模式的文件名保存為一個list列表。
返回匹配 pathname 的可能為空的路徑名列表,其中的元素必須為包含路徑信息的字符串。
pathname 可以是絕對路徑 (如 /usr/src/Tools/sub/1.gif) 或相對路徑 (如 ../../Tools/*/*.gif),并且可包含 shell 風(fēng)格的通配符。
實例
一個包含以下內(nèi)容的目錄:文件 1.gif, 2.txt, card.gif 以及一個子目錄 sub 其中只包含一個文件 3.txt。glob()將產(chǎn)生如下結(jié)果。請注意路徑的任何開頭部分都將被保留。
fileinput與glob配合使用
輸出
更多使用方法請參見 fileinput模塊[2]。
codecs
常用open方法操作文件,寫入str類型,不管字符串是什么編碼方式,此時一般不會出現(xiàn)什么問題。但有時候我們爬蟲或者其他方式得到一些數(shù)據(jù)寫入文件時會有編碼不統(tǒng)一的問題,或在自然語言處理過程中,使用open方法操作文件會經(jīng)常出現(xiàn)報錯,通常是編碼錯誤。
此時如若想繼續(xù)使用 open 方式打開,就需要先將輸入文件decode,統(tǒng)一轉(zhuǎn)為unicode ,再encode到目標(biāo)編碼方式,如gbk、utf-8等等。即
幸運的是,可以使用 codecs.open() 代替這一繁瑣操作。這種方法可以指定一個編碼打開文件,讀取返回的將是unicode。
codecs.open()
使用給定的 mode 打開已編碼的文件并返回一個 StreamReaderWriter 的實例,提供透明的編碼/解碼。
mode參數(shù)可以是內(nèi)置 open()函數(shù)所接受的任意二進(jìn)制模式,默認(rèn)的文件模式為 'r',表示以讀取模式打開文件。'b' 會被自動添加。
encoding指定文件所要使用的編碼格式。允許任何編碼為字節(jié)串或從字節(jié)串解碼的編碼格式,而文件方法所支持的數(shù)據(jù)類型則取決于所使用的編解碼器。
寫入時,如果參數(shù)是unicode,則使用open()時指定的編碼進(jìn)行編碼后寫入;如果是str,則先根據(jù)源代碼文件聲明的字符編碼,解碼成unicode后再進(jìn)行前述操作。
相對內(nèi)置的open()來說,這個方法比較不容易在編碼上出現(xiàn)問題,并且在速度不變的同時,還兼容open()函數(shù)所有操作命令。
應(yīng)用實例
將未知編碼方式的csv文件轉(zhuǎn)為utf-8格式文件。
更多相關(guān)方法可參見**codecs模塊[3]**。
CSV
CSV[4] (Comma Separated Values),即逗號分隔值(也稱字符分隔值,因為分隔符可以不是逗號),是一種常用的文本格式,用以存儲表格數(shù)據(jù),包括數(shù)字或者字符。很多程序在處理數(shù)據(jù)時都會碰到csv這種格式的文件。
python內(nèi)置了csv模塊。常用的有四個方法:
csv.reader
返回一個reader對象,該對象將逐行遍歷csvfile 。
svfile可以是任何對象,文件對象和列表對象均適用。如果 csvfile 是文件對象,則打開它時應(yīng)使用 newline=''。
dialect用于不同的 CSV 變種的特定參數(shù)組。
fmtparams可以覆寫當(dāng)前變種格式中的單個格式設(shè)置。有關(guān)變種和格式設(shè)置參數(shù)的完整詳細(xì)信息,請參見 變種與格式參數(shù)[5] 。
應(yīng)用實例
輸出
以上用到的變種與格式參數(shù)解釋如下。
delimiter一個用于分隔字段的單字符,默認(rèn)為 ' , '。
quotechar一個單字符,用于包住含有特殊字符的字段,特殊字符如 定界符 或 引號字符 或 換行符。默認(rèn)為 ' " '。
由于使用 open()來讀取 CSV 文件,因此默認(rèn)情況下,將使用系統(tǒng)默認(rèn)編碼來解碼文件并轉(zhuǎn)換為unicode,要使用其他編碼來解碼文件,可使用open的encoding參數(shù):
csv.writer
返回一個 writer 對象,該對象將用戶的數(shù)據(jù)在給定的文件類對象上轉(zhuǎn)換為帶分隔符的字符串。None 值會寫入為空字符串。
寫入前,所有非字符串?dāng)?shù)據(jù)都先用 str() 轉(zhuǎn)化為字符串再寫入。
csvfile 可以是具有 write() 方法的任何對象。
應(yīng)用案例
輸出csv文件打開如下
參數(shù)quoting說明:控制 writer 何時生成引號,以及 reader 何時識別引號。該屬性可以等于任何 QUOTE_* 常量,默認(rèn)為QUOTE_MINIMAL。
QUOTE_\* 常量包括:
csv.QUOTE_ALL指示writer 對象給所有字段加上引號。
csv.QUOTE_MINIMAL指示writer 對象僅為包含特殊字符(例如定界符、引號字符或行結(jié)束符中的任何字符)的字段加上引號。
csv.QUOTE_NONNUMERIC指示writer 對象為所有非數(shù)字字段加上引號。指示reader 將所有未用引號引出的字段轉(zhuǎn)換為float 類型。
csv.QUOTE_NONE指示writer 對象不使用引號引出字段。當(dāng)定界符出現(xiàn)在輸出數(shù)據(jù)中時,其前面應(yīng)該有轉(zhuǎn)義符。如果未設(shè)置轉(zhuǎn)義符,則遇到任何需要轉(zhuǎn)義的字符時,writer 都會拋出 Error 異常。指示reader 不對引號字符進(jìn)行特殊處理。
csv.DictReader
創(chuàng)建一個對象,該對象在操作上類似于常規(guī) reader,但是將每行中的信息映射到一個 dict,該 dict 的鍵由 fieldnames 可選參數(shù)給出。
fieldnames參數(shù)是一個 sequence。如果省略 **fieldnames**,則文件 f 第一行中的值將用作字段名。無論字段名是如何確定的,字典都將保留其原始順序。
如果某一行中的字段多于字段名,則剩余數(shù)據(jù)會被放入一個列表,并與 restkey 所指定的字段名 (默認(rèn)為 None) 一起保存。如果某個非空白行的字段少于字段名,則缺失的值會使用 restval 的值來填充 (默認(rèn)為 None)。
應(yīng)用實例
注意:
在 3.6 版更改: 返回的行現(xiàn)在的類型是 OrderedDict。在 3.8 版更改: 現(xiàn)在,返回的行是 dict類型。
python 3.6/3.7輸出:
python 3.8輸出
Reader 對象的一些方法
csvreader.line_num源迭代器已經(jīng)讀取了的行數(shù)。它與返回的記錄數(shù)不同,因為記錄可能跨越多行。
csvreader.fieldnames字段名稱。如果在創(chuàng)建對象時未傳入字段名稱,則首次訪問時或從文件中讀取第一條記錄時會初始化此屬性。
輸出
csv.DictWriter
創(chuàng)建一個對象,該對象在操作上類似常規(guī) writer,但會將字典映射到輸出行。
fieldnames 參數(shù)是由鍵組成的 sequence,它指定字典中值的順序,這些值會按指定順序傳遞給 writerow() 方法并寫入文件 f 。
如果字典缺少 fieldnames 中的鍵,則可選參數(shù) restval 用于指定要寫入的值。
如果傳遞給 writerow() 方法的字典的某些鍵在 fieldnames 中找不到,則可選參數(shù) extrasaction 用于指定要執(zhí)行的操作。如果將其設(shè)置為默認(rèn)值 'raise',則會引發(fā) ValueError。如果將其設(shè)置為 'ignore',則字典中的其他鍵值將被忽略。
應(yīng)用案例
寫入的csv文件打開結(jié)果如下
Writer 對象的一些方法
DictWriter.writeheader()在 writer 的文件對象中,寫入一行字段名稱(字段名稱在構(gòu)造函數(shù)中指定),并根據(jù)當(dāng)前設(shè)置的變種進(jìn)行格式化。
csvwriter.writerow(row)將參數(shù) row 寫入 writer 的文件對象。
csvwriter.writerows(rows)將 rows (即能迭代出多個上述 row 對象的迭代器)中的所有元素寫入 writer 的文件對象
更多相關(guān)方法可參見csv模塊[6]。
第三方模塊
另外還有專門針對Word文件、Excel文件及PDF文件的模塊,這里只簡單介紹下。
word文件
python-docx
PDF文件
pdfplumber
Excel文件
比較常用的Excel操作模塊有如下三種。
- xlrd 讀取.xls和.xlsx文件
- xlwings 讀取.xls和.xlsx文件
- openpyxl 讀取.xlsx文件
xlrd
xlrd不能創(chuàng)建和保存Excel文件
xlwings
xlwings 直接對接的是 Excel 應(yīng)用程序,然后才是工作簿 books 和工作表 sheets,xlwings 需要安裝有 Excel 應(yīng)用程序的環(huán)境xlwings 可以讀取 .xls 和 .xlsx 文件。
openpyxl
openpyxl 可以讀取 .xlsx 文件,如果讀取.xls 文件會報錯。
另外還有很強大的pandas模塊,具體可參見Python數(shù)據(jù)分析實戰(zhàn)之?dāng)?shù)據(jù)獲取三大招,本文不在贅述。
參考資料
[1]pathlib模塊: https://docs.python.org/zh-cn/3/library/pathlib.html
[2]fileinput模塊: https://docs.python.org/zh-cn/3/library/fileinput.html
[3]codecs模塊: https://docs.python.org/zh-cn/3/library/codecs.html
[4]CSV: http://zh.wikipedia.org/zh-cn/逗號分隔值
[5]變種與格式參數(shù): https://docs.python.org/zh-cn/3/library/csv.html#csv-fmt-params
[6]csv模塊: https://docs.python.org/zh-cn/3/library/csv.html#index-0