筆試問題
如何使用Python讀取1個8GB大小的檔案,這個問題其實在筆試中會經常遇到的1個題目。對於在Python中讀取檔案的操作,一般我們會這樣來操作:
1 2 |
f = open('filename','rb') f.read() |
下面我們來找1個比較大的檔案,比如1個nginx的日誌檔案,記得之前有一次公司的1天的nginx日誌檔案解壓為3GB大小,不得不對其進行切分。
發現問題
這裡我們找到了1個3G大小的檔案。接下來,我們使用普通的讀取方式來檢視該檔案的內容:
1 2 3 4 5 6 |
f=open('test','rb') data=f.read() --------------------------------------------------------------------------- MemoryError Traceback (most recent call last) ... MemoryError: |
我們可以看到1個MemoryError的錯誤,說明該無檔案無法被裝載在記憶體中發生溢位了。
下面我們來思考下為什麼記憶體會溢位了,在我們開啟檔案的時候並沒有發生任何異常,而在我們呼叫read方法時才出現問題。我們知道,檔案物件的read方法會嘗試將所有內容以1行的形式讀入,顯然這種方式對於大檔案是不可行的。
解決方案
在Python中,除了使用read方法讀取檔案內容外,還有另外2個方法readline和readlines也可以進行內容的讀取。
既然預設read方法是一次性的將內容都讀取到記憶體中,那麼我們是否可以指定其每次讀取的長度來解決這個問題呢?
1 2 3 4 |
data = f.read(1024) while 1: #處理該行的程式碼 data = f.read(1024) |
而readlines會返回每1行讀取的內容的列表,因此有一定風險的。
1 2 |
for l in f.readlines(): #處理這1行的程式碼 |
那麼,我們每次讀取1行總可以了把。這樣我們可以通過如下的方式來進行:
1 2 3 4 |
line = f.readline() while 1: #處理該行的程式碼 line = f.readline() |
我們通過1個無限迴圈的方式來進行讀取。結果發現,使用readlines的方式還是會導致記憶體不足的情況發生,而通過讀取指定位元組的方式則可以處理完這個檔案。
在上面的解決方案中,我們需要手動處理檔案讀取的大小,並在合適的情況退出讀取的操作。
那麼,我們有沒有更好的解決方案呢?實際上是有的,在Python的手冊中,有1個xreadlines的方法,這個方法就類比range和xrange函式的區別。這個方法返回類似iter(f)
的字串,但是遺憾的是該方法在Python版本2.3中已經被淘汰了,官方建議我們使用for語句來替代:
1 2 |
for line in f: #處理該行的程式碼 |
通過這種方式,Python將處理檔案物件為1個迭代器,並自動使用快取IO和記憶體管理,這樣我們就不需要關注大的檔案了。
參考檔案:
http://stackoverflow.com/questions/8009882/how-to-read-large-file-line-by-line-in-python