1. 程式人生 > >Python 按行讀取文字檔案 快取 和 非快取實現

Python 按行讀取文字檔案 快取 和 非快取實現

需求

最近專案中有個讀取檔案的需求,資料量還挺大,10萬行的數量級。

java 使用快取讀取檔案是,會相應的建立一個內部緩衝區陣列在java虛擬機器記憶體中,因此每次處理的就是這一整塊記憶體。

簡單的想:就是如果不用快取,每次都要硬碟–虛擬機器快取–讀取;有了快取,提前讀了一段放在虛擬機器快取裡,可以避免頻繁將硬碟上的資料讀到快取裡。

因為對記憶體的操作肯定是比硬碟的操作要快的。

對了,java還有對映記憶體,可以解決大檔案讀寫的問題。

思路

大檔案讀寫不能一次全部讀入記憶體,這樣會導致耗盡記憶體。(但是在記憶體允許的情況下,全部讀入記憶體是不是速度更快??)
對於大檔案可以一行一行讀取,因為我們處理完這行,就可以把它拋棄。

我們也可以一段一段讀取大檔案,實現一種快取處理。每次讀取一段檔案,將這段檔案放在快取裡,然後對這段處理。這會比一行一行快些。

方法1:一行一行讀取

我們可以開啟一個檔案,然後用for迴圈讀取每行,比如:

def method1(newName):
    s1 = time.clock()
    oldLine = '0'
    count = 0
    for line in open(newName):
        newLine =  line
        if (newLine != oldLine):
            #判斷是不是空行
            if
newLine.strip(): nu = newLine.split()[0] oldLine = newLine count += 1 print "deal %s lines" %(count) e1 = time.clock() print "cost time " + str(e1-s1)

我們測試一下

fileName = 'E:\\pythonProject\\ruisi\\correct_re.txt'
method1(fileName)

輸出

deal 218376
lines cost time 0.288900734402

方法1.1 一行一行讀取的變形

def method11(newName):
    s1 = time.clock()
    oldLine = '0'
    count = 0
    file = open(newName)
    while 1:
        line = file.readline()
        if not line:
            break
        else:
            if line.strip():
                newLine =  line
                if (newLine != oldLine):
                    nu = newLine.split()[0]
                    oldLine = newLine
                    count += 1
    print "deal %s lines" %(count)
    e1 = time.clock()
    print "cost time " + str(e1-s1)
deal 218376 lines
cost time 0.371977884619

耗時和方法1差不多,比方法1稍微多些。

方法2:一行一行,使用fileinput模組

def method2(newName):
    s1 = time.clock()
    oldLine = '0'
    count = 0
    for line in fileinput.input(newName):
        newLine =  line
        if newLine.strip():
            if (newLine != oldLine):
                nu = newLine.split()[0]
                oldLine = newLine
                count += 1
    print "deal %s lines" %(count)
    e1 = time.clock()
    print "cost time " + str(e1-s1)
deal 218376 lines
cost time 0.514534051673

這兒的耗時差不多是方法1的兩倍。

藉助快取,每次讀取1000行

def method3(newName):
    s1 = time.clock()
    file = open(newName)
    oldLine = '0'
    count = 0
    while 1:
        lines = file.readlines(10*1024)
        #print len(lines)
        if not lines:
            break
        for line in lines:
            if line.strip():
                newLine =  line
                if (newLine != oldLine):
                    nu = newLine.split()[0]
                    oldLine = newLine
                    count += 1
    print "deal %s lines" %(count)
    e1 = time.clock()

Note
readlinessizehint() 引數是限定位元組大小,不是行數。
注意預設有個內部緩衝區大小是8KB,如果設定值小於 8*1024。那麼都是按照8KB來的。print len(lines)輸出大概都為290。
只有當設定值大於8KB,上面的print len(lines)才會發生變化。

deal 218376 lines
cost time 0.296652349397

這兒的效能還沒方法1,表現好。可以調整每次讀取的行數,比如500,1000等等,可以達到不同的耗時。

方法4 一次性全部讀到記憶體裡

def method4(newName):
    s1 = time.clock()
    file = open(newName)
    oldLine = '0'
    count = 0
    for line in file.readlines():
        if line.strip():
            newLine =  line
            if (newLine != oldLine):
                nu = newLine.split()[0]
                oldLine = newLine
                count += 1
    print "deal %s lines" %(count)
    e1 = time.clock()
    print "cost time " + str(e1-s1)

輸出

deal 218376 lines
cost time 0.30108883108

結論

推薦使用

with open('foo.txt', 'r') as f:
    for line in f:
        # do_something(line)

對於大檔案可以使用索引,這個索引記錄下每行開頭的位置,之後就可以用file.seek()定位了。如果檔案內容修改了,還需要重新建立索引。這個索引可以有很多種方法建立,但是都需要將檔案遍歷一次。

參考資料