Joh*_*eks 5 python regex stream
我有数据流连续进入许多TCP套接字.对于每一个,我有一个不同的正则表达式,我需要拉出匹配.例如,可以匹配格式##.#的数字,后跟字母f:
r = re.compile(rb'([0-9][0-9]\.[0-9])f')
Run Code Online (Sandbox Code Playgroud)
另一个可能匹配字母Q前面的###格式的数字:
r = re.compile(rb'Q([0-9][0-9][0-9])')
Run Code Online (Sandbox Code Playgroud)
实际上,表达式可能具有任意长度和复杂性,并且从配置文件中提取并且事先不知道.它们不是硬编码的.
当新数据进入时,我将其附加到类型的缓冲区bytearray()(此处称为self.buffer).然后我调用这样的函数(self.r作为编译的正则表达式):
def advance(self):
m = self.r.search(self.buffer)
# No match. Return.
if m is None:
return None
# Match. Advance the buffer and return the matched groups.
self.buffer = self.buffer[m.end():]
return m.groups()
Run Code Online (Sandbox Code Playgroud)
如果还没有匹配,则返回None.如果匹配,则返回匹配并丢弃缓冲区直到匹配结束,使其自己准备好再次调用.
但是,这种方法并不是特别有效.问题是必须一次又一次地扫描缓冲区 - 每当有新数据进入时 - 直到找到匹配为止.在找到匹配项并且可以提前启动缓冲区之前,可能会重复扫描数千次和数百万个字符.
在找到匹配项之前,我不能简单地丢弃缓冲区的内容,因为匹配可能需要缓冲区的最后几个字节(甚至整个缓冲区).一旦有更多数据进入,缓冲区的结尾可能是匹配的开始.
如何重写我的"advance"函数以安全地丢弃可能永远不会包含正则表达式开头的缓冲区部分,以便不需要重复扫描整个缓冲区?
一种可能性:如果未找到匹配的原因是因为字符串的结尾已到达,是否有"搜索"的替代方法返回"无"以外的内容?如果是这样,我可以获得潜在匹配的起始位置吗?这将允许我丢弃缓冲区到那一点.
另一种可能性:某种类型的库足够聪明,可以重写任意正则表达式,因此它们可以在截断的字符串上以不同的 - 可检测的 - 方式匹配.
我也会接受其他可能性,但他们需要使用任意正则表达式,而不仅仅是上面的简单表达式.理想情况下,他们也不会涉及两次扫描缓冲区(一次找到实际的潜在匹配,一次丢弃东西).
3rd-partyregex模块(不是re)提供部分匹配支持,这是部分解决方案。(当您尝试放弃窗口的开头并继续搜索时,后视、^锚点、零宽度匹配和\b/锚点都会以微妙或不那么微妙的方式中断。我想到了多少边缘情况\B到目前为止,如果还有更多,我不会感到惊讶。)
如果您传递partial=True给regex.match、regex.search、regex.fullmatch或regex.finditer,那么除了报告普通的完整匹配之外,它还会报告不匹配的内容,但如果字符串被扩展,则可以报告:
In [8]: import regex
In [9]: regex.search(r'1234', '', partial=True)
Out[9]: <regex.Match object; span=(0, 0), match='', partial=True>
In [10]: regex.search(r'1234', '12', partial=True)
Out[10]: <regex.Match object; span=(0, 2), match='12', partial=True>
In [11]: regex.search(r'1234', '12 123', partial=True)
Out[11]: <regex.Match object; span=(3, 6), match='123', partial=True>
In [12]: regex.search(r'1234', '1234 123', partial=True)
Out[12]: <regex.Match object; span=(0, 4), match='1234'>
Run Code Online (Sandbox Code Playgroud)
您可以使用匹配对象的属性来确定匹配是部分匹配还是完全匹配partial:
In [13]: regex.search(r'1234', '12 123', partial=True).partial
Out[13]: True
In [14]: regex.search(r'1234', '1234 123', partial=True).partial
Out[14]: False
Run Code Online (Sandbox Code Playgroud)
如果更多数据可能会改变匹配结果,它将报告为部分匹配:
In [21]: regex.search(r'.*', 'asdf', partial=True)
Out[21]: <regex.Match object; span=(0, 4), match='asdf', partial=True>
In [22]: regex.search(r'ham(?: and eggs)?', 'ham', partial=True)
Out[22]: <regex.Match object; span=(0, 3), match='ham', partial=True>
Run Code Online (Sandbox Code Playgroud)
或者如果更多数据可能导致匹配不匹配:
In [23]: regex.search(r'1(?!234)', '1', partial=True)
Out[23]: <regex.Match object; span=(0, 1), match='1', partial=True>
In [24]: regex.search(r'1(?!234)', '13', partial=True)
Out[24]: <regex.Match object; span=(0, 1), match='1'>
Run Code Online (Sandbox Code Playgroud)
当到达数据流末尾时,您应该关闭partial以告知regex已结束,因此部分匹配不会隐藏完整匹配。
有了部分匹配信息,您可以丢弃部分匹配开始之前的所有内容,并且知道丢弃的数据不会出现在匹配中……但是后向查找可能需要这些数据,因此需要进行一些额外的工作如果您正在这样做,请支持lookbehind。^也会因字符串变化的开头而感到困惑,\b/\B不知道丢弃的数据末尾是否有单词字符,并且无论对于什么定义,要获得正确的零宽度匹配行为都是很棘手的“正确”你选择。regex我怀疑如果您以这种方式丢弃数据,其他一些高级功能也可能会产生奇怪的交互;regex有很多功能。