一、如何將列表數(shù)據(jù)寫入文件
⾸先,我們來看看下⾯這段代碼,并思考:這段代碼有沒有問題,如果有問題的話,要怎么改?
li = ['python',' is',' a',' cat']
with open('test.txt','w') as f:
f.write(li)
現(xiàn)在公布答案,這段代碼會報錯:
TypeError Traceback (most recent call last)
ipython-input-6-57e0c2f5a453> in module>()
1 with open('test.txt','w') as f:
----> 2 f.write(li)
TypeError: write() argument must be str, not list
以上代碼的想法是將list列表內容寫⼊txt⽂件中,但是報錯 TypeError: write() argument must be str。
就是說,write()⽅法必須接受字符串(str)類型的參數(shù)。 Python中內置了str()⽅法,可以返回字符串版本的對象(Return a string version of object)。所以,上⾯的例⼦中,我們試試把 f.write(li) 改為 f.write(str(li)) ,先做⼀下字符串類型的轉化看看。代碼略。 這次沒有報錯了,但是打開⽂件就傻眼了吧,寫⼊的內容是“['python',' is',' a',' cat']”。怎么才能寫 成“python is a cat”呢? ⽂件寫操作還有⼀個writelines()⽅法,它接收的參數(shù)是由字符串組成的序列(sequence),實際寫⼊的效果是將全部字符串拼接在⼀起。字符串本身也是⼀種序列,所以當參數(shù)是字符串的時候,writelines()⽅法等價于write()。
# 以下3種寫法等價,都是寫⼊字符串“python is a cat”
In [20]: with open('test.txt','w') as f:
...: f.writelines(['python',' is',' a',' cat'])
...: f.writelines('python is a cat')
...: f.write('python is a cat')
# 以下2種寫法等價,都是寫⼊列表的字符串版本“['python',' is',' a',' cat']”
In [21]: with open('test.txt','w') as f:
...: f.write(str(['python',' is',' a',' cat']))
...: f.writelines(str(['python',' is',' a',' cat']))
# 作為反例,以下寫法都是錯誤的:
In [22]: with open('test.txt','w') as f:
...: f.writelines([2018,'is','a','cat']) # 含⾮字符串
...: f.write(['python','is','a','cat']) # ⾮字符串
由上可知,當多段分散的字符串存在于列表中的時候,要⽤writelines()⽅法,如果字符串是⼀整段,那直 接使⽤write()⽅法。如果要以整個列表的形式寫⼊⽂件,就使⽤str()⽅法做下轉化。 這個問題還沒結束,如果列表中就是有元素不是字符串,⽽且要把全部元素取出來,怎么辦呢? 那就不能直接使⽤write()和writelines()了,需要先⽤for循環(huán),把每個元素取出來,逐⼀str()處理。
In [37]: content=[1,' is',' everything']
In [38]: with open('test.txt','w') as f:
...: for i in content:
...: f.write(str(i))
需要注意的是,writelines()不會⾃動換⾏。如果要實現(xiàn)列表元素間的換⾏,⼀個辦法是在每個元素后⾯加 上換⾏符“\n”,如果不想改變元素,最好是⽤for循環(huán),在寫⼊的時候加在末尾:for i in content: f.writelines(str(i)+“\n”) 引申⼀下,經(jīng)過實驗,數(shù)字及元祖類型也可以作為write()的參數(shù),不需轉化。但是dict字典類型不可以, 需要先⽤str()處理⼀下。字典類型⽐較特殊,最好是⽤json.dump()⽅法寫到⽂件。 總結⼀下,write()接收字符串參數(shù),適⽤于⼀次性將全部內容寫⼊⽂件;writelines()接收參數(shù)是由字符串 組成的序列,適⽤于將列表內容逐⾏寫⼊⽂件。str()返回Python對象的字符串版本,使⽤需注意。
二、如何從文件中讀取內容?
從⽂件中讀取內容有如下⽅法:
file.read([size])
從⽂件讀取指定的字節(jié)數(shù),如果未給定或為負則讀取所有。
file.readline([size])
讀取整⾏,包括 "\n" 字符。
file.readlines([sizeint])
讀取所有⾏并返回列表,若給定sizeint>0,則是設置⼀次讀多少字節(jié),這是為了減輕讀取壓⼒。
簡⽽⾔之,在不傳參數(shù)的情況下,read()對應write(),讀取全部內容;readlines()對應writelines(),讀取 全部內容(含換⾏符)并以列表形式返回,每個換⾏的內容作為列表的⼀個元素。
In [47]: with open('test.txt','r') as f:
...: print(f.read())
1 is everything.
python is a cat.
this is the end.
In [48]: with open('test.txt','r') as f:
...: print(f.readlines())
['1 is everything.\n', 'python is a cat.\n', 'this is the end.']
但是,以上兩個⽅法有個缺點,當⽂件過⼤的時候,⼀次性讀取太多內容,會對內存造成極⼤壓⼒。讀操作還有⼀個readline()⽅法,可以逐⾏讀取。
In [49]: with open('test.txt','r') as f: ...: print(f.readline()) 1 is everything.
readline()讀取第⼀⾏就返回,再次調⽤f.readline(),會讀取下⼀⾏。 這么看來,readline()太笨拙了。那么,有什么辦法可以優(yōu)雅地讀取⽂件內容呢? 回過頭來看readlines()⽅法,它返回的是⼀個列表。這不奇怪么,好端端的內容為啥要返回成列表呢? 再想想writelines()⽅法,把字符串列表寫⼊⽂件正是這家伙⼲的事,readlines()⽅法恰恰是它的逆操作! ⽽writelines()⽅法要配合for循環(huán),所以我們把readlines()與for循環(huán)結合,看看會怎樣。
In [61]: with open('test.txt','r') as f:
...: for line in f.readlines():
...: print(line)
1 is everything.
python is a cat.
this is the end.
# 讀取內容包含換⾏符,所以要strip()去掉換⾏符
In [62]: with open('test.txt','r') as f:
...: for line in f.readlines():
...: print(line.strip())
1 is everything.
python is a cat.
this is the end.
總結⼀下,readline()⽐較雞肋,不咋⽤;read()適合讀取內容較少的情況,或者是需要⼀次性處理全部內容的情況;⽽readlines()⽤的較多,⽐較靈活,因為for循環(huán)是⼀種迭代器,每次加載部分內容,既減少內 存壓⼒,⼜⽅便逐⾏對數(shù)據(jù)處理。
三、多樣需求的讀寫任務
前兩部分講了⽂件讀寫的⼏⼤核⼼⽅法,它們能夠起作⽤的前提就是,需要先打開⼀個⽂件對象,因為只有在⽂件操作符的基礎上才可以進⾏讀或者寫的操作。 打開⽂件⽤的是open()⽅法,所以我們再繼續(xù)講講這個⽅法。open() ⽅法⽤于打開⼀個⽂件,并返回⽂件對象,在對⽂件進⾏處理過程都需要使⽤到這個函數(shù),如果該⽂件⽆法被打開,會拋出 OSError。
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
open()⽅法的參數(shù)⾥file(⽂件)是必需的,其它參數(shù)最常⽤的是mode(模式)和encoding(編碼)。 先說說encoding,⼀般來說,打開⽂件的編碼⽅式以操作系統(tǒng)的默認編碼為準,中⽂可能會出現(xiàn)亂碼,需要加encoding='utf-8'。
In [63]: with open('test.txt','r') as f:
...: for line in f.readlines():
...: print(line.strip())
-----------------------
UnicodeDecodeError Traceback (most recent call last)
ipython-input-63-731a4f9cf707> in module>()
1 with open('test.txt','r') as f:
----> 2 for line in f.readlines():
3 print(line.strip())
UnicodeDecodeError: 'gbk' codec can't decode byte 0xa4 in positio n 26: illegal multibyte sequence
In [65]: with open('test.txt','r',encoding='utf-8') as f:
...: for line in f.readlines():
...: print(line.strip())
python is a cat.
再說mode,它指定⽂件打開的模式。
r': 以只讀模式打開(缺省模式)(必須保證⽂件存在)
'w':以只寫模式打開。若⽂件存在,則清空⽂件,然后重新創(chuàng)建;若不存在,則新建⽂件。
'a':以追加模式打開。若⽂件存在,則會追加到⽂件的末尾;若⽂件不存在,則新建⽂件。
常⻅的mode組合
'r'或'rt': 默認模式,⽂本讀模式
'w'或'wt': 以⽂本寫模式打開(打開前⽂件會被清空)
'rb': 以⼆進制讀模式打開 'ab': 以⼆進制追加模式打開
'wb': 以⼆進制寫模式打開(打開前⽂件會被清空)
'r+': 以⽂本讀寫模式打開,默認寫的指針開始指在⽂件開頭, 因此會覆寫⽂件
'w+': 以⽂本讀寫模式打開(打開前⽂件會被清空)
'a+': 以⽂本讀寫模式打開(寫只能寫在⽂件末尾)
'rb+': 以⼆進制讀寫模式打開
'wb+': 以⼆進制讀寫模式打開(打開前⽂件會被清空)
'ab+': 以⼆進制讀寫模式打開
初看起來,模式很多,但是,它們只是相互組合罷了。建議記住最基本的w、r、a,遇到特殊場景,再翻看⼀下就好了。
四、從with語句到上下文管理器
基礎部分講完了,下⾯是進階部分。知其然,更要知其所以然。
1、with語句是初學者必會常識
⾸先,要解釋⼀下為啥前⽂直接就⽤了with語句。with語句是讀寫⽂件時的優(yōu)雅寫法,這已經(jīng)默認是Python初學者必會的常識了。如果你還不會,先看看⽤和不⽤with語句的對⽐:
# 不⽤with語句的正確寫法
try:
f = open('test.txt','w')
f.writelines(['python',' is',' a',' cat'])
finally:
if f:
f.close()
# 使⽤with語句的正確寫法
with open('test.txt','w') as f:
f.writelines(['python',' is',' a',' cat'])
因為⽂件對象會占⽤操作系統(tǒng)的資源,并且操作系統(tǒng)同⼀時間能打開的⽂件數(shù)量是有限的,所以open()⽅法之后⼀定要調⽤close()⽅法。另外,讀寫操作可能出現(xiàn)IO異常的情況,所以要加try...finally,保證⽆論如何,都會調⽤到close()⽅法。 這樣寫萬⽆⼀失,但是實在繁瑣,⼀不⼩⼼還可能漏寫或者寫錯。⽽with語句會保證調⽤close(),只需⼀⾏代碼,簡直不要太優(yōu)雅!所以,with語句是Python初學者必會技能。
2、什么是上下⽂管理器?
下⾯,重頭戲來了,什么是上下⽂管理器(context manager)?
上下⽂管理器是這樣⼀個對象:它定義程序運⾏時需要建⽴的上下⽂,處理程序的進⼊和退出,實現(xiàn)了上下⽂管理協(xié)議,即在對象中定義了 __enter__() 和 __exit__() ⽅法。 __enter__():進⼊運⾏時的上下⽂,返回運⾏時上下⽂相關的對象,with 語句中會將這個返回值綁定到⽬標對象。 __exit__(exception_type, exception_value, traceback):退出運⾏時的上下⽂,定義在塊執(zhí)⾏(或終⽌)之后上下⽂管理器應該做什么。它可以處理異常、清理現(xiàn)場或者處理 with 塊中語句執(zhí)⾏完成之后需要處理的動作。
注意 enter 和 exit 的前后有兩個下劃線,Python 中⾃帶了很多類似的⽅法,它們是很神秘⼜很強⼤的存在,江湖⼈常常稱其為“⿊魔法”。例如,迭代器協(xié)議就實現(xiàn)了__iter__⽅法。 在Python的內置類型中,很多類型都是⽀持上下⽂管理協(xié)議的,例如 file、thread.LockType、 threading.Lock 等等。上下⽂管理器⽆法獨⽴使⽤,它們要與 with 相結合,with 語句可以在代碼塊運⾏前進⼊⼀個運⾏時上下⽂(執(zhí)⾏__enter__⽅法),并在代碼塊結束后退出該上下⽂(執(zhí)⾏__exit__⽅法)。 with 語句適⽤于對資源進⾏訪問的場合,確保不管使⽤過程中是否發(fā)⽣異常都會執(zhí)⾏必要的“清理”操作,釋放資源,⽐如⽂件使⽤后⾃動關閉、線程中鎖的⾃動獲取和釋放等。
3、⾃定義上下⽂管理器
除了Python的內置類型,任何⼈都可以定義⾃⼰的上下⽂管理器。下⾯是⼀個示例:
class OpenFile(object):
def __init__(self,filename,mode):
def open_file(name):
ff = open(name, 'w')
ff.write("enter now\n")
try:
yield ff
except RuntimeError:
pass
ff.write("exit now")
ff.close()
with open_file('test.txt') as f:
f.write('Hello World!\n')
最終寫⼊⽂件的結果是:
enter now Hello World! exit now
上下⽂管理器必須同時提供 enter() 和 exit() ⽅法的定義,缺少任何⼀個都會導致 AttributeError。 上下⽂管理器在執(zhí)⾏過程中可能會出現(xiàn)異常,exit() 的返回值會決定異常的處理⽅式:返回值等于 False,那么這個異常將被重新拋出到上層;返回值等于 True,那么這個異常就被忽略,繼續(xù)執(zhí)⾏后⾯的代碼。exit() 有三個參數(shù)(exception_type, exception_value, traceback),即是異常的相關信息。
4、contextlib實現(xiàn)上下⽂管理器
上例中,⾃定義上下⽂管理器的寫法還是挺繁瑣的,⽽且只能⽤于類級別。為了更好地輔助上下⽂管理,Python 內置提供了 contextlib 模塊,進⽽可以很⽅便地實現(xiàn)函數(shù)級別的上下⽂管理器。 該模塊本質上是通過裝飾器(decorators)和⽣成器(generators)來實現(xiàn)上下⽂管理器,可以直接作⽤于函數(shù)/對象,⽽不⽤去關⼼ enter() 和 exit() ⽅法的具體實現(xiàn)。 先把上⾯的例⼦改造⼀下,然后我們再對照著解釋:
from contextlib import contextmanager
@contextmanager
def open_file(name):
ff = open(name, 'w')
ff.write("enter now\n")
try:
yield ff
except RuntimeError:
pass
ff.write("exit now")
ff.close()
with open_file('test.txt') as f:
f.write('Hello World!\n')
contextmanager 是要使⽤的裝飾器,yield 關鍵字將普通的函數(shù)變成了⽣成器。yield 的返回值(ff)等于上例__enter__()的返回值,也就是 as 語句的值(f),⽽ yield 前后的內容,分別是__enter__() 和__exit__() ⽅法⾥的內容。 使⽤ contextlib,可以避免類定義、__enter__() 和 __exit__() ⽅法,但是需要我們捕捉可能的異常(例如,yield 只能返回⼀個值,否則會導致異常 RuntimeError),所以 try...except 語句不能忽略。
到此這篇關于Python初學者必備的文件讀寫指南的文章就介紹到這了,更多相關Python文件讀寫內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- python實現(xiàn)簡單文件讀寫函數(shù)
- 詳解python中的異常和文件讀寫
- 使用Python文件讀寫,自定義分隔符(custom delimiter)
- Python基于codecs模塊實現(xiàn)文件讀寫案例解析
- python文件處理--文件讀寫詳解