2012-09-26 73 views
4

我試圖在文件中尋找一個令牌':path',然後將所有以下(任意數字)數字作爲數字讀取(因此對於':path, 123'我尋找,在文件然後讀取整數123)。然後讀取當前搜索位置和pos + 123之間的字符(將它們存儲在列表中或其他內容中)。然後尋找,直到':path'的下一個匹配項,然後重複這個過程。使用python在大文件中尋找正則表達式

我想的功能有點像:

 
def fregseek(FILE, current_seek, /regex/): 

. 
. 
    value_found = ? # result of reading next N chars after :path,[0-9]+ 
. 
. 
    return next_start_seek, value_found 

可以有任何數量的匹配:在一條線上「路徑」,並且之後指定字符數內可能出現的字符串' 「。我寫了一堆亂七八糟的垃圾,在每一行中讀取,然後對於匹配指示的前N個字符的每一行,然後繼續處理字符串,直到它全部被吃掉。然後讀取下一個字符串等。

這太可怕了,我不想從一個潛在的巨大文件中汲取所有的線,當我真正需要做的就是尋找(尤其是因爲換行是無關緊要的,所以有一個額外的處理步驟只是因爲線很容易從文件中拉出是荒謬的)。

所以,那就是,這是我想解決的問題。我需要尋找一個匹配,讀取一個值,從該值的末尾繼續尋找下一個匹配,等等,直到文件耗盡。

如果有人能幫助我,我會很樂意向他們聽到:)

我想避免不規範的庫如果可能的話,我也想在最短的代碼,但是這是最的我的擔憂(速度和內存消耗是重要的因素,但我不想讓50個loc更多,只是爲了引導一些庫中有一個小功能,如果只知道它是什麼的話我就可以剔除)。我寧願使用python代碼,但是,如果perl在這方面跳動python,我將使用perl,而我也打開聰明的sed/awk/bash腳本等,只要它們不會非常慢。

非常感謝。

+0

是否需要使用正則表達式?如果你只是試圖尋找一個像「:path」這樣的標記,那就沒有必要了,如果你只是做一個字符串搜索,它會更容易(也更高效)。 – abarnert

+0

此外,你一直在談論尋找,但沒有掃描所有字節沒有辦法搜索,我沒有看到任何你不能做的一次通過,所以我不知道爲什麼你需要任何尋求一切。 – abarnert

+0

感謝您的寶貴意見。如果我不必一次讀入整個文件,字符串搜索就沒有問題,但是我必須高效地處理讀入的任何塊。我不確定是否有更好的方法比全部讀取更好,儘管我希望能夠處理任意大的文件。理想情況下,我想有幾個選擇的基準,但現在我只是有我的蹩腳的代碼,有已經回答很多比我現在:) – sillyMunky

回答

3

如果你不需要正則表達式,你可以隨便找個和切片做到這一點。

無論哪種方式,瑣碎的解決方案是將整個文件讀取到內存中,並找到並切片生成的結果爲str/bytes對象。

但是,如果您不能(或不想)將整個文件讀入內存,則這不起作用。幸運的是,如果你可以指望你的文件是< < 2GB,或者你只需​​要使用64位Python,而且你在一個合理的平臺(POSIX,現代Windows等)上,您可以將mmap文件改爲內存。 mmap對象有一個字符串具有的相同方法的子集,所以你可以假裝你有一個字符串,就像你將整個文件讀入內存一樣,但是你可以指望Python實現和操作系統來實現它只是合理的工作效率。

根據您的Python版本,re可能無法像掃描一個字符串一樣掃描mmap,但它可能工作但速度很慢,或者它可能工作得很好。所以,你不妨試試,首先,如果沒有拋出異常或者去比你預期的,你做慢得多:

def findpaths(fname): 
    with open(fname, 'rb') as f: 
    m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) 
    for match in re.finditer(':path,([0-9]+)', m): 
     yield m[match.end():match.end()+int(match.group(1))] 

(這是一樣的BrtH的回答,只是用一個mmap而不是一個字符串,並重構爲一個生成器而不是一個列表 - 雖然當然你可以通過用括號替換他的方括號來完成後面的部分。)

如果你使用的是較舊的(或非-CPython)Python版本不能(有效)remmap,這是一個比較複雜一點:

def nextdigits(s, start): 
    return ''.join(itertools.takewhile(str.isdigit, 
            itertools.islice(s, start, None))) 

def findpaths(fname): 
    with open(fname, 'rb') as f: 
    m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) 
    i = 0 
    while True: 
     n = m.find(':path', i) 
     if n == -1: return 
     countstr = nextdigits(m, n+6) 
     count = int(countstr) 
     n += 6 + len(countstr) 
     yield m[n:n+count] 
     i = n + 6 + count 

這可能不是編寫nextdigits函數的最快方法。我不確定這實際上是否重要(時間和看到),但如果它確實如此,其他可能性是將m[n+6:n+A_BIG_ENOUGH_NUMBER]切片並將其正則表達,或編寫自定義循環,或者......另一方面,如果這是您的瓶頸,你也許可以在一個JIT(PyPy,Jython中,或IronPython的)切換到解釋得更加受益...

對於我的測試,我拆東西:findpaths採用繩狀物體,且調用者的with openmmap位,並且僅將m傳遞給findpaths;爲了簡潔,我沒有在這裏做。

無論如何,我測試過兩個版本對以下數據:

BLAH:path,3abcBLAH:path,10abcdefghijklmnBLAH:path,3abc:path,0:path,3abc 

輸出功率爲:

abc 
abcdefghij 
abc 

abc 

我認爲這是正確的嗎?

如果我的早期版本導致它以100%CPU旋轉,我的猜測是我沒有在循環中正確增加i;這是您在嚴格解析循環中獲得該行爲的最常見原因。無論如何,如果您可以使用當前版本進行復制,請發佈數據。

+0

感謝您的建議,我喜歡返回發電機的想法。它不適合我,因爲某些原因,當我真正嘗試使用返回的生成器時,我得到的執行速度非常快,沒有任何事情發生或咀嚼我的所有系統資源並需要查殺(使用非常小的測試文件)。你能告訴我你是如何使用它的嗎? – sillyMunky

+0

這是一個很好的答案,並且可能比我的要求更好,所以+1。 – BrtH

+1

但有一件事我不明白。你似乎認爲伯爵已經知道並且是不變的。但是,如果我正確地理解了這個問題,事實並非如此,你還必須找到計數。除非計數總是有三位數字,否則你必須用正則表達式找到它。我認爲你可以使用'i = n + 7',因爲':path,{至少一個數字}'這個詞不能重疊。 – BrtH

2

你可以做到這一點在近一個行蟒蛇:

with open('filename.txt') as f: 
    text = f.read() 

results = [text[i[0]:i[0] + i[1]] for i in 
      ((m.end(), int(m.group(1))) for m in 
      re.finditer(':path,([0-9]+)', text))] 

注:未經測試...

+0

這對我來說很適合小文件,非常感謝!我贊成,因爲這是一個很好的答案,比我有效得多。我支持一個不需要一次讀取整個文件的答案,但可以處理任意大的文件(也許使用mmap?)。如果我在這裏找不到它,我會接受你的,因爲它幫助我繼續處理更廣泛的文件,雖然沒有達到我的要求的頂峯(任意文件大小,但額外成本不高)......不小訂購!)。再次感謝您的貢獻:) – sillyMunky