在我們日常工作中,難免會有處理日志文件的時候,當文件小的時候,基本不用當心什么,直接用file.read()或readlines()就可以了,但是如果是將一個10G大小的日志文件讀取,即文件大於內存的大小,這么處理就有問題了,會將整個文件加載到內存中從而造成MemoryError … 也就是發生內存溢出。
下面分享幾個解決辦法:
對file對象進行迭代處理:
with open('file_name', 'r') as file: for line in file: print line
優點:
-
with語句,文件對象在執行完代碼塊退出后自動關閉文件流,文件讀取數據發生異常,進行異常捕獲處理
-
對文件對象進行迭代時,在內部,它會緩沖IO(針對昂貴的IO操作進行優化)和內存管理,所以不必擔心大文件。
-
這才是
Pythonci
最完美的方式,既高效又快速
缺點:每一行的數據內容不能大於內存大小,否則就會造成MemoryError
使用yield
正常情況使用上面這種方式就可以了,But
,如果遇到整個文件只有一行,而且按照特定的字符進行分割,上面這種方式則不行了,這時候yield就非常有用了。
舉個栗子,log的形式是這樣子的。
-
2018-06-18 16:12:08,289 - main - DEBUG - Do something{|}…..
-
以{|}做為分割符。
-
def read_line(filename, split, size): with open(filename, 'r+') as file: buff = '' while True: while split in buff: position = buff.index(split) yield buff[:position] buff = buff[(position +len(split)):] chunk = file.read(size) if not chunk: yield buff break buff = buff +chunk
優點:不在限制每行數據的大小,即使整個大文件只有一行。
缺點:速度比上面這種方式要慢。
解析一下:
-
首先:定義一個緩沖區buff
-
循環判斷,如果split分割符在緩沖區buff,則進行查找分割符出現的位置,並yield回去。
-
將buff更新,繼續第二步
-
如果split分割符不在緩沖區buff,則read(size)個字符
-
如果chunk為空,則跳出循環,否則更新buff, 繼續第二步
所以我們需要使用那種方式呢,一般來說使用用第一種就可以了。碰到只有一行的數據,而且數據特別大的,就要考慮一下你是不是得罪那個程序員了,故意給你這樣一個文件。