2017-05-27 43 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感谢您的评论,你是对的,我已经用另一种方法更新了答案 –