Python正则表达式解析流
Posted
技术标签:
【中文标题】Python正则表达式解析流【英文标题】:Python regex parse stream 【发布时间】:2011-06-05 18:48:45 【问题描述】:有没有办法在 python 中的流上使用正则表达式匹配? 喜欢
reg = re.compile(r'\w+')
reg.match(StringIO.StringIO('aa aaa aa'))
我不想通过获取整个字符串的值来做到这一点。我想知道是否有任何方法可以匹配 srtream(on-the-fly)上的正则表达式。
【问题讨论】:
@SlientGhost:不一定。您可能希望使用正则表达式解析一些(无限)流,始终在流的当前开头匹配并将匹配项作为迭代器返回(并仅使用从流中匹配的字符)。 @MartinStettner:好吧,如果它是一个没有反向引用的自动机理论匹配器(以及其他一些东西,例如前瞻约束),你可以。只要 RE 可以编译为单个有限自动机(NFA 或 DFA),它就可以一次匹配事物,因此可以处理发现匹配无限流。 (但 Python 使用 PCRE,它不是自动机理论的,它需要更早的所有字节。) @DonalFellows 我查看了pcre.org/pcre.txt 并没有发现 PCRE 算法不是基于自动机理论的迹象。当然,为了实现反向引用和前瞻,它需要维护一个内部缓冲区,但这不会阻止某种机制,例如,某种needmore
回调工作,(在许多情况下,缓冲区不需要与可能的无限流大小相比非常大)。
@MartinStettner:这是一些人“只知道”的事情之一。基于堆栈的匹配器可以支持更丰富的语言——这就是你真正所说的——但需要一个它们可以在其中备份的令牌流。 (我想这是我在读计算机科学本科时研究这些东西的原因。)
太糟糕了,这不是现成的。显然,需要解决一些(严重的)边缘情况,例如反向引用,但从概念上讲,这肯定是一个合理的事情。我想一个提供re
功能子集的专用实用程序可能是有意义的。
【参考方案1】:
这里的答案现在已经过时了。现代 Python re
包现在支持 bytes-like 对象,这些对象有一个 API,您可以自己实现并获得流行为。
【讨论】:
【参考方案2】:在文件的特定情况下,如果您可以使用mmap
对文件进行内存映射,并且使用字节串而不是Unicode,则可以将内存映射文件提供给re
,就好像它一样是一个字节串,它会正常工作。这受限于您的地址空间,而不是您的 RAM,因此具有 8 GB RAM 的 64 位计算机可以很好地映射 32 GB 文件。
如果你能做到这一点,这是一个非常好的选择。如果你不能,你必须转向更混乱的选择。
第 3 方 regex
模块(不是 re
)提供部分匹配支持,可用于构建流媒体支持......但它很混乱并且有很多警告。诸如lookbehinds 和^
之类的东西不起作用,零宽度匹配很难正确处理,而且我不知道它是否能与regex
提供的其他高级功能正确交互,而re
不能.尽管如此,它似乎是最接近可用的完整解决方案的东西。
如果您将partial=True
传递给regex.match
、regex.fullmatch
、regex.search
或regex.finditer
,那么除了报告完全匹配之外,regex
还将报告可能匹配的内容,如果数据已扩展:
In [10]: regex.search(r'1234', '12', partial=True)
Out[10]: <regex.Match object; span=(0, 2), match='12', partial=True>
如果更多数据可能改变匹配结果,它将报告部分匹配而不是完全匹配,例如,regex.search(r'[\s\S]*', anything, partial=True)
将始终是部分匹配。
有了这个,你可以保持一个滑动的数据窗口来匹配,当你到达窗口的末尾时扩展它,并从一开始就丢弃消耗的数据。不幸的是,任何会被从字符串开头消失的数据弄糊涂的东西都行不通,所以后面的^
、\b
和\B
都出局了。零宽度匹配也需要小心处理。这是一个在文件或类似文件的对象上使用滑动窗口的概念证明:
import regex
def findall_over_file_with_caveats(pattern, file):
# Caveats:
# - doesn't support ^ or backreferences, and might not play well with
# advanced features I'm not aware of that regex provides and re doesn't.
# - Doesn't do the careful handling that zero-width matches would need,
# so consider behavior undefined in case of zero-width matches.
# - I have not bothered to implement findall's behavior of returning groups
# when the pattern has groups.
# Unlike findall, produces an iterator instead of a list.
# bytes window for bytes pattern, unicode window for unicode pattern
# We assume the file provides data of the same type.
window = pattern[:0]
chunksize = 8192
sentinel = object()
last_chunk = False
while not last_chunk:
chunk = file.read(chunksize)
if not chunk:
last_chunk = True
window += chunk
match = sentinel
for match in regex.finditer(pattern, window, partial=not last_chunk):
if not match.partial:
yield match.group()
if match is sentinel or not match.partial:
# No partial match at the end (maybe even no matches at all).
# Discard the window. We don't need that data.
# The only cases I can find where we do this are if the pattern
# uses unsupported features or if we're on the last chunk, but
# there might be some important case I haven't thought of.
window = window[:0]
else:
# Partial match at the end.
# Discard all data not involved in the match.
window = window[match.start():]
if match.start() == 0:
# Our chunks are too small. Make them bigger.
chunksize *= 2
【讨论】:
【参考方案3】:这似乎是一个老问题。正如我在a similar question 上发布的那样,您可能希望将我的解决方案streamsearch-py 的Matcher 类子类化并在缓冲区中执行正则表达式匹配。查看 kmp_example.py 以获取模板。如果事实证明经典的 Knuth-Morris-Pratt 匹配就是您所需要的,那么您的问题现在将通过这个小型开源库得到解决:-)
【讨论】:
【参考方案4】:我遇到了同样的问题。第一个想法是实现一个LazyString
类,它的作用类似于一个字符串,但只从流中读取当前需要的尽可能多的数据(我通过重新实现__getitem__
和__iter__
来获取和缓冲字符直到访问的最高位置...)。
这没有成功(我从re.match
得到一个“TypeError:预期的字符串或缓冲区”),所以我稍微研究了标准库中re
模块的实现。
不幸的是,在流上使用正则表达式似乎是不可能的。模块的核心是用 C 语言实现的,这个实现期望整个输入一次在内存中(我猜主要是因为性能原因)。似乎没有简单的方法可以解决此问题。
我还查看了PYL(Python LEX/YACC),但他们的词法分析器在内部使用re
,所以这不能解决问题。
一种可能是使用支持 Python 后端的ANTLR。它使用纯 Python 代码构建词法分析器,并且似乎能够对输入流进行操作。由于对我来说这个问题并不那么重要(我不希望我的输入非常大......),我可能不会进一步调查,但它可能值得一看。
【讨论】:
经过深入研究,很有趣。也许acooke.org/rxpy 是一个合理的选择? 我刚刚找到了另一个解决方案:pexpect (pexpect.readthedocs.org/en/latest/api/pexpect.html) (我在上面发表评论多年后)现在有一种方法可以做到这一点***.com/a/69652161/55935【参考方案5】:是的 - 使用 getvalue
方法:
import cStringIO
import re
data = cStringIO.StringIO("some text")
regex = re.compile(r"\w+")
regex.match(data.getvalue())
【讨论】:
嗯,这和给它一个字符串是一样的,我想知道是否有任何方法可以解析一个流以上是关于Python正则表达式解析流的主要内容,如果未能解决你的问题,请参考以下文章