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.matchregex.fullmatchregex.searchregex.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正则表达式解析流的主要内容,如果未能解决你的问题,请参考以下文章

将正则表达式解析为 AST 的 Python 库?

python正则表达式解析(re)

Python从零开始写爬虫-2 使用正则表达式解析HTML

Python爬虫解析神器-正则表达式如何正确运用?案例详解

Python爬虫解析神器-正则表达式如何正确运用?案例详解

python爬虫--解析网页几种方法之正则表达式