在python中,re.finditer是否有办法将文件作为输入而不是字符串?

在python中,re.finditer是否有办法将文件作为输入而不是字符串?,python,regex,file-io,Python,Regex,File Io,假设我有一个非常大的文件foo.txt,我想在找到正则表达式后对它进行迭代。目前我这样做: f = open('foo.txt') s = f.read() f.close() for m in re.finditer(regex, s): doSomething() 有没有一种方法可以做到这一点,而不必将整个文件存储在内存中 注意:逐行读取文件不是一个选项,因为正则表达式可能跨越多行 更新:如果可能的话,我也希望它能与stdin一起使用 更新:我正在考虑用自定义文件包装器模拟字符串对

假设我有一个非常大的文件
foo.txt
,我想在找到正则表达式后对它进行迭代。目前我这样做:

f = open('foo.txt')
s = f.read()
f.close()
for m in re.finditer(regex, s):
    doSomething()
有没有一种方法可以做到这一点,而不必将整个文件存储在内存中

注意:逐行读取文件不是一个选项,因为正则表达式可能跨越多行

更新:如果可能的话,我也希望它能与stdin一起使用


更新:我正在考虑用自定义文件包装器模拟字符串对象,但我不确定正则表达式函数是否会接受自定义的类似字符串的对象。

也许您可以编写一个生成一行(读取一行)的函数在读取文件时,调用re.finditer,直到它产生EOF信号。

您必须按块读取文件,重叠以允许表达式的最大可能长度,或者使用一个mmapped文件,其工作原理几乎与使用流一样好:

更新到您的更新: 考虑STDIN不是一个文件,它的行为很像一个文件,它有一个文件描述符等等。这是一个posix流。如果你不清楚两者之间的区别,可以在谷歌上搜索一下。操作系统无法映射它,因此python无法

也认为你所做的可能是不适合使用正则表达式的。正则表达式非常适合捕捉小东西,比如解析连接字符串、日志条目、csv数据等等。它们不是一个解析海量数据的好工具。这是故意的。您最好编写一个自定义解析器

一些来自过去的智慧之言:

如果可以将正则表达式可以跨越的行数限制在合理的范围内,则可以使用
collections.deque
在文件上创建滚动窗口,并在内存中仅保留该行数

from collections import deque

def textwindow(filename, numlines):
    with open(filename) as f:
        window   = deque((f.readline() for i in xrange(numlines)), maxlen=numlines)
        nextline = True
        while nextline:
            text = "".join(window)
            yield text
            nextline = f.readline()
            window.append(nextline)

 for text in textwindow("bigfile.txt", 10):
     # test to see whether your regex matches and do something

这里是另一个解决方案,使用内部文本缓冲区逐步生成找到的匹配项,而无需将整个文件加载到内存中

from collections import deque

def textwindow(filename, numlines):
    with open(filename) as f:
        window   = deque((f.readline() for i in xrange(numlines)), maxlen=numlines)
        nextline = True
        while nextline:
            text = "".join(window)
            yield text
            nextline = f.readline()
            window.append(nextline)

 for text in textwindow("bigfile.txt", 10):
     # test to see whether your regex matches and do something
这个缓冲区就像一个通过文件文本的“滑动窗口”,向前移动,同时产生找到的匹配项

由于文件内容是按块加载的,这意味着此解决方案也适用于多行正则表达式

def find_chunked(fileobj, regex, *, chunk_size=4096):
    buffer = ""

    while 1:
        text = fileobj.read(chunk_size)
        buffer += text
        matches = list(regex.finditer(buffer))

        # End of file, search through remaining final buffer and exit
        if not text:
            yield from matches
            break

        # Yield found matches except the last one which is maybe 
        # incomplete because of the chunk cut (think about '.*')
        if len(matches) > 1:
            end = matches[-2].end()
            buffer = buffer[end:]
            yield from matches[:-1]

但是,请注意,如果没有找到任何匹配项,它可能最终会将整个文件加载到内存中,因此如果您确信文件多次包含正则表达式模式,则最好使用此函数。

如果正则表达式跨越多行怎么办?也许您可以修改yield函数以生成2或3(或任何看似合理的数字)一次一行,每次迭代一次,推一条新的行,然后删除最新的行来控制它。谢谢,不幸的是,它不适用于诸如“代码> STDIN < /代码>之类的特殊文件。还有一种方法也适用于<代码> STDIN < /代码>吗?简短答案:不,你不能MMAP STDIN。长回答:考虑正则表达式如何工作,W。回溯和所有寻找他们所做的。现在考虑在一个流上做这件事。它听起来还是一个好主意吗?=类似地,MMAP STDIN将不会有多大意义,打开一个完整的问题。不要认为正则表达式需要回溯,除非它们不是规则的。我知道你不能MSTMAP STDIN或任何。stream,这就是为什么我问是否有其他方法。另外,请参阅此相关问题,以获取使用
mmap
实现的示例:我不能真正限制行数,因为正则表达式可能包含类似
'\n*'
的内容。在这种情况下,是的,您必须阅读整个文件。@Matt:当然,即使有什么东西像
“\n*”
是允许的,必须有某种实际的/现实的/现实世界的限制可以设置……例如
n
新行与
n+1
新行是否真的有显著的不同,因为
n
的值很大?@martineau当然我可以选择一个任意大的限制,但这会导致即使是小的匹配,我也会使用大量内存,这正是我试图避免的。我想找到一个解决方案,只存储
re
模块当时需要的内容。我希望有某种方法可以将迭代器传递给
re.finditer
函数,但似乎没有这样做的方法。看起来
re
不遵循duck键入模式,并专门检查字符串是否为
str
,否则我可以用自己的类模拟字符串。不幸的是,正则表达式引擎(与所有正则表达式引擎一样)是为性能而优化的,因此它不会在每次需要新行时调用迭代器。即使它调用了,并且您编写了
\n*
,您仍然可以在内存中保存整个文件(因为正则表达式可能需要回溯)。FWIW我刚才做的一个简单测试似乎表明
re.finditer()
将接受
str
的子类。因此,如果您可以确定并模拟
finditer()
使用的所有
str
方法,则可以使用该方法。另一种可能是对类包装器进行子类化(或将其用作您自己类的模板).Source应该在Python安装的../lib/UserString.py文件中。一切似乎都围绕着在内部操纵
self.data
属性,因此您可以在自己的子类中连接到该属性。如果没有其他内容,它看起来像是自定义
str
子类的一个很好的实现指南。@martineau I想想看,
UserString
的全部目的是在内置无法被子类化的情况下对
str
进行子类化。这可能是真的,但它仍然存在,如果没有其他东西的话,它看起来很有用,可以作为所需方法的简明指南……也许可以实现基于以下答案之一的检索数据的方法。