在阅读之前懒惰地过滤文件

And*_*den 5 python file

假设我有一个BIG文件,其中包含一些我想忽略的行,以及一个file_function带有文件对象的函数().我可以返回一个新的文件对象,其行满足某些条件而不首先读取整个文件,这种懒惰是重要的部分.

注意:我可以保存一个忽略这些行的临时文件,但这并不理想.

例如,假设我有一个csv文件(带有坏行):

1,2
ooops
3,4
Run Code Online (Sandbox Code Playgroud)

第一次尝试是创建新文件对象(使用与文件相同的方法)并覆盖readline:

class FileWithoutCondition(file):
    def __init__(self, f, condition):
        self.f = f
        self.condition = condition
    def readline(self):
        while True:
            x = self.f.readline()
            if self.condition(x):
                return x
Run Code Online (Sandbox Code Playgroud)

如果file_name仅使用readline...则有效,但如果需要其他功能则不行.

with ('file_name', 'r') as f:
    f1 = FileWithoutOoops(f, lambda x: x != 'ooops\n')
    result = file_function(f1)
Run Code Online (Sandbox Code Playgroud)

使用StringIO的解决方案可能有效,但我似乎无法实现.

理想情况下,我们应该假设这file_function是一个黑盒功能,特别是我不能只是调整它来接受一个生成器(但也许我可以调整一个生成器就像文件一样?).
有没有一种标准的方法来执行这种懒惰(skim-)读取通用文件?

注意:这个问题的激励性例子就是这个熊猫问题,其中只有一个readline不足以开始pd.read_csv工作......

Mic*_*kis 1

将映射缩减方法与现有的 Python 工具结合使用。在此示例中,我使用正则表达式来匹配以 string 开头的行GET /index,但您可以使用适合您的账单的任何条件:

import re
from collections import defaultdict

pattern = re.compile(r'GET /index\(.*\).html')

# define FILE appropriately.
# map
# the condition here serves to filter lines that can not match.
matches = (pattern.search(line) for line in file(FILE, "rb") if 'GET' in line)
mapp    = (match.group(1) for match in matches if match)

# now reduce, lazy:
count = defaultdict(int)
for request in mapp:
    count[request] += 1
Run Code Online (Sandbox Code Playgroud)

在我的笔记本电脑上,这会在几秒钟内扫描>6GB的文件。您可以进一步将文件分割成块并将它们提供给线程或进程。我不建议使用mmap,除非您有足够的内存来映射整个文件(它不支持窗口)。