1. 程式人生 > 其它 >python讀取大檔案的幾種方法

python讀取大檔案的幾種方法

在 python 中,當我們讀取一個本地 TextIO 檔案時,最常用的方式是用readreadlinereadlines這三個方法。

fp.readlines()

with open(fp_name) as f_read:
    data = f_read.readlines()    # The type of data is a list.

# output:
# ["<?xml version='1.0' encoding='utf8'?>\n", ...]

這種方式是將檔案裡面所有內容按行讀取到一個大列表中。對於小檔案,這種方式其實挺方便,但對於大檔案就會出現記憶體可能不足的情況,報MemoryError

錯誤,或者消耗掉很客觀的記憶體資源。

fp.readline()

# 第一種寫法
with open(fp_name) as f_read:
    while True:
        line = f_read.readline()    # The type of line is a string.
        if not line:
            break

# 第二種寫法
with open(fp_name) as f_read:
    for line in f_read:
        ...

上面兩種寫法本質其實一樣,都是按行讀取文字,這種方式讀取檔案會大大減少記憶體的消耗,一直讀到空行或者 EOF 標識才會被終止。一般大場景讀取檔案這種方式效能和記憶體消耗比較好。

但這種方式也有一個缺陷,若文字是寫在一行,而不是多行,那麼這兩種寫法不論哪一種,都和前面兩種方式一樣,將所有的文字內容【一行字串】載入到記憶體當中,消耗大量的資源。那麼這個時候處理單行十分大的檔案,使用fp.read()這個最為底層的方法更好一些。

fp.read()

with open(fp_name) as f_read:
    data = f_read.read()    # The type of data is a string.

# output
# '<?xml version=\'1.0\' encoding=\'utf8\'?>\n<OpenDRIVE><header revMajor="1" revMinor="4" name="" version="1.00" date="01/01/1970 17:45\n...\n'

不帶引數的情況下,這種方式將文字內容讀取為一個大的字串物件,類似readlines()方法,只不過是輸出資料的格式不同。如果文字比較大,該方式會消耗很客觀的記憶體。

不過該方法有一個size引數,用於設定讀取文字的位元組數,每次呼叫 fp.read (size) 會直接返回從當前位置往後讀取size大小的檔案內容,不必等待任何換行符出現,這種方式有利於對單行大文字進行讀取處理。

我們可以使用這個分塊引數這麼讀取大檔案,效果要比按行讀取的方式在記憶體消耗上優化很多:

from functools import partial, wraps
from typing import TextIO, Callable


def chunked_file_reader(fp: TextIO, block_size: int=1024 * 8):
    for chunk in iter(partial(fp.read, block_size), ''):
        yield chunk


def read_file(file_path: str) -> int:
    count: int = 0
    with open(file_path) as f_read:
        for chunk in chunked_file_reader(f_read):
            count += 1
            print(chunk)
    return count

利用迭代器生成器構造一個可複用的分塊讀取方法,然後就可以方便的控制每次讀取的位元組大小,在記憶體的佔用以及程式碼的執行效能上都會有不錯的表現。