需要注意的是文件讀寫(xiě)完成后必須及時(shí)關(guān)閉文件,一方面打開(kāi)的文件會(huì)占用操作系統(tǒng)的資源,并且操作系統(tǒng)同一時(shí)間能打開(kāi)的文件數(shù)量也是有限制的,比如Linux操作系統(tǒng)中我們可以使用ulimit -n命令查看多可打開(kāi)文件的數(shù)量。另一方面在寫(xiě)文件時(shí),操作系統(tǒng)是把數(shù)據(jù)放到內(nèi)存緩沖區(qū)異步寫(xiě)入磁盤(pán)中,并不會(huì)立刻把數(shù)據(jù)全部寫(xiě)入磁盤(pán),而調(diào)用close()方法可以使操作系統(tǒng)把沒(méi)有寫(xiě)入磁盤(pán)的數(shù)據(jù)全部寫(xiě)入磁盤(pán)中,防止數(shù)據(jù)丟失的情況。接下來(lái)我們先看下正確的文件打開(kāi)方式。
文件打開(kāi)的幾種方式
Python內(nèi)置了open()方法打開(kāi)了一個(gè)文件,如下所示。文件打開(kāi)模式有'r'、'w'、'a'、'r+'、'w+'、'a+'、'b'等,'r'只讀模式打開(kāi)文件,并將文件指針指向文件頭,如果文件不存在會(huì)報(bào)錯(cuò);'w'只寫(xiě)模式打開(kāi)文件,并將文件指針指向文件頭,如果文件存在則將其內(nèi)容清空,不存在則創(chuàng)建;'a'以只追加可寫(xiě)模式打開(kāi)文件,并將文件指針指向文件尾部,如果文件不存在則創(chuàng)建。對(duì)應(yīng)于open()方法打開(kāi)文件需要有close()方法關(guān)閉文件。
f = open('/mnt/media/log.txt', 'r') f.read() f.close()
由于讀寫(xiě)文件時(shí)都有可能產(chǎn)生IOError,比如文件不存在的情況,此時(shí)open()方法會(huì)拋出一個(gè)IOError的異常,那么后面的f.close()就不會(huì)被調(diào)用。為了保證無(wú)論是否出錯(cuò)都能正確地關(guān)閉文件,我們可以使用try ... finally來(lái)實(shí)現(xiàn)。
try: f = open('/mnt/media/log.txt', 'r') f.read() finally: if f: f.close()
由于try ... finally方式實(shí)現(xiàn)較為繁瑣,Python引入了with語(yǔ)句會(huì)自動(dòng)調(diào)用f.close()方法,使得代碼更簡(jiǎn)潔。
with open('/mnt/media/log.txt', 'r') as f: f.read()
大文件讀取幾種方式
對(duì)文件的讀取操作是將文件中的數(shù)據(jù)加載到內(nèi)存中,那么對(duì)于大文件的讀取,如果一次把文件中全部的內(nèi)容全部加載到內(nèi)存中顯然會(huì)耗盡系統(tǒng)的內(nèi)容。我們看下Python中讀取文件常用的方法read()、readline()、readlines()對(duì)于大文件讀取的支持情況:read(size)方法是從文件當(dāng)前位置起讀取size個(gè)字節(jié),若無(wú)參數(shù)size,則表示讀取至文件結(jié)束為止,如果文件比較小,用read()一次讀取文件較為方便,但如果不能確定文件大小,反復(fù)調(diào)用read(size)比較保險(xiǎn);readline()方法每次讀出一行內(nèi)容,所以讀取時(shí)占用內(nèi)存小,比較適合大文件。readlines()方法讀取整個(gè)文件所有行,保存在一個(gè)列表list變量中,每行作為一個(gè)元素,讀取大文件時(shí)比較占內(nèi)存。
說(shuō)到大文件的讀取,有個(gè)linecache模塊,這里要說(shuō)明下的是這個(gè)模塊的優(yōu)勢(shì)是通過(guò)緩存文件內(nèi)容的方式來(lái)加快下次讀取文件的速度,所以需要耗費(fèi)更多的內(nèi)存,那么以下是我在Linux發(fā)行版LEDE+MT7688的環(huán)境下對(duì)readlines、linecache.getlines以及遍歷文件這三種方式在內(nèi)存的使用情況下的對(duì)比:
count = len(open(filepath, 'r').readlines()) _________________________________________________________ count = = len(linecache.getline(filepath) ) _________________________________________________________ count = 0 for count, line in enumerate(open(filepath,'r')): pass count += 1 ________________________________________________ count = len([ "" for line in open("filename","r")])
不打開(kāi)文件:Mem: 37648K used, 88184K free, 116K shrd, 0K buff, 12540K cached
readlines讀取文件:Mem: 69560K used, 56272K free, 124K shrd, 0K buff, 27004K cached
linecache.getlines讀取文件:Mem: 70396K used, 55436K free, 116K shrd, 0K buff, 26996K cached
遍歷方式讀取文件:Mem: 53032K used, 72800K free, 116K shrd, 0K buff, 27668K cached
但是linecache.getlines在讀取文件的速度上是有優(yōu)勢(shì)的,因?yàn)槲募?nèi)容已經(jīng)緩存在內(nèi)存中了,下次讀取可以直接從內(nèi)存中獲取,可以使用linecache.checkcache檢測(cè)文件在磁盤(pán)上是否發(fā)生了變化,如果變化了需要使用linecache.updatecache更新緩存。不過(guò)讀取文件需要打開(kāi)文件,對(duì)于一個(gè)15M左右20000行的日志文件三種方式差不多需要8、9秒的時(shí)間,但第二次讀取文件linecache.getlines方式是微秒級(jí)的。
readlines讀取文件: time count 215794 type1 is 9.58759188652 time count 215794 type1 is 1.70862102509 time count 215794 type1 is 2.05462002754 time count 215794 type1 is 1.69754505157 time count 215813 type1 is 2.1633579731 time count 215813 type1 is 1.61879992485 遍歷方式讀取文件: time count 215508 type2 is 8.8404238224 time count 215508 type3 is 2.22844409943 time count 215508 type2 is 2.19772100449 time count 215508 type3 is 2.57516384125 time count 215586 type2 is 2.12095785141 time count 215586 type3 is 2.55960321426 time count 215586 type2 is 2.1704659462 time count 215586 type3 is 2.11596107483 linecache.getlines讀取文件: time count 214811 type4 is 8.19337201118 time count 214811 type4 is 6.50882720947e-05 time count 214811 type4 is 9.41753387451e-05 time count 214811 type4 is 6.69956207275e-05 time count 214811 type4 is 9.41753387451e-05 time count 214811 type4 is 6.89029693604e-05