2017-05-27 44 views
6

巨大的純文本數據文件

我讀了使用python塊一個巨大的文件。然後我在該塊上應用正則表達式。基於標識符標記,我想提取相應的值。由於塊大小,數據在塊邊界處缺失。的Python:跨文件塊的邊界正則表達式匹配

要求:

  • 文件必須以塊的形式讀取。
  • 塊大小必須小於 或等於1 GiB。


Python代碼示例

identifier_pattern = re.compile(r'Identifier: (.*?)\n') 
with open('huge_file', 'r') as f: 
    data_chunk = f.read(1024*1024*1024) 
    m = re.findall(identifier_pattern, data_chunk) 


組塊數據的例子

良好:數量的標籤等效於值的數量

標識符:價值
標識符:價值
標識符:價值
標識符:價值


由於塊大小,你就會得到不同的邊界問題,如下所示。第三個標識符返回一個不完整的值,「v」代替「value」。下一個塊包含「alue」。這會導致解析後丟失的數據。

壞:標識值不完整

標識符:價值
標識符:價值
標識符:v


你怎麼解決塊邊界問題這樣嗎?

+0

也許你可以在這裏找到你的答案:[Python正則表達式解析流](https://stackoverflow.com/questions/4634376/python-regex-parse-stream) –

+0

也在這裏:[正則表達式在流而不是字符串?(https://stackoverflow.com/questions/13004359/regular-expression-on-stream-instead-of-string) –

+0

因爲你的模式行邊界上出現,也許你可以只一次讀取一行並在行上而不是塊上匹配。 – Himanshu

回答

3

假設這是您的具體問題,你很可能只是適應你的正則表達式和讀取一行行(這將不完整的文件加載到內存中):

import re 
matches = [] 
identifier_pattern = re.compile(r'Identifier: (.*?)$') 
with open('huge_file') as f: 
    for line in f: 
     matches += re.findall(identifier_pattern, line) 

print("matches", matches) 
+0

良好的低內存佔用解決方案。該文件不是基於行的,如所示的例子所示。我沒有明確規定要求。我不得不明確指定文件必須分塊讀取。以某種方式,我必須在塊邊界處找到解決方案,同時避免意外的重複計數。 – JodyK

0

我有非常相似的傑克的解決方案回答:

#!/usr/bin/env python3 

import re 

identifier_pattern = re.compile(r'Identifier: (.*)$') 

m = [] 
with open('huge_file', 'r') as f: 
    for line in f: 
     m.extend(identifier_pattern.findall(line)) 

你可以使用正則表達式API的另一部分具有相同的結果:

#!/usr/bin/env python3 

import re 

identifier_pattern = re.compile(r'Identifier: (.*)$') 

m = [] 
with open('huge_file', 'r') as f: 
    for line in f: 
     pattern_found = identifier_pattern.search(line) 
     if pattern_found: 
      value_found = pattern_found.group(0) 
      m.append(value_found) 

其中我們可以簡化使用generator expressionlist comprehension

#!/usr/bin/env python3 

import re 

identifier_pattern = re.compile(r'Identifier: (.*)$') 

with open('huge_file', 'r') as f: 
    patterns_found = (identifier.search(line) for line in f) 
    m = [pattern_found.group(0) 
     for pattern_found in patterns_found if pattern_found] 
+0

我同意這些是基於行的文件的很好解決方案。假設我們有一個嚴格的條件,那就是我們'必須'以塊的形式讀取文件:是否有解決塊邊界問題的可能解決方案? – JodyK

+0

這些示例基於您的示例。但是對於每次迭代,您是否可以保留模式可能出現的前一個塊的最後幾個字符? – EvensF

+0

我沒有清楚的塊需求。你的建議接近安德里的方法。我想這是解決這個問題的最接近的方法。恐怕不可能在後面的塊或後面的塊中進行一種前瞻。逐行方法消除了人們對大塊的多處理優勢。 – JodyK

1

如果該文件是基於行的,所述file目的是線的懶惰發生器,它將文件加載到存儲器線通過線(中塊),基於這一點,你可以使用:

import re 
matches = [] 
for line in open('huge_file'): 
    matches += re.findall("Identifier:\s(.*?)$", line) 
+1

對於基於行的文件來說,這確實是一個很好的解決方案。是否還有一個解決方案,該文件不是基於行的,並且您必須閱讀塊? – JodyK

2

您可以控制大塊成型,並將它接近1024 * 1024 * 1024,在如果你避免遺漏部分:

import re 


identifier_pattern = re.compile(r'Identifier: (.*?)\n') 
counter = 1024 * 1024 * 1024 
data_chunk = '' 
with open('huge_file', 'r') as f: 
    for line in f: 
     data_chunk = '{}{}'.format(data_chunk, line) 
     if len(data_chunk) > counter: 
      m = re.findall(identifier_pattern, data_chunk) 
      print m.group() 
      data_chunk = '' 
    # Analyse last chunk of data 
    m = re.findall(identifier_pattern, data_chunk) 
    print m.group() 

Alternativelly,您可以(從第一次:0,第二次從第一次迭代過程中收集匹配的字符串的最大長度)去兩次以上與read不同的出發點相同的文件,將結果存儲爲字典,其中key=[start position of matched string in file],那個位置對於每次迭代都是相同的,所以合併結果應該不是問題,但是我認爲通過匹配字符串的開始位置和長度進行合併會更準確。

祝你好運!

+0

這是一個非常聰明的方法,最接近我想要的。我沒有這樣想過。然而,基於行的閱讀將對多處理塊形成新的挑戰。這就是爲什麼我更喜歡f.read()方法並將塊分割爲單獨的進程。逐行同步將是非常昂貴的進程間操作。 – JodyK

+0

@JodyK感謝您的評論,你是對的,我已經用另一種方法更新了答案 –