我想以最小的开发成本使用re
模块和流,但不一定是文件流。
对于文件流,有mmap
模块可以模拟字符串,因此可以re
自由使用。
现在我想知道mmap
如何设法制作re
可以进一步重用的对象。如果我只是传递任何内容,re
保护自己免于使用与TypeError: expected string or bytes-like object
太不兼容的对象。所以我想我会创建一个派生自string
或bytes
的类,并覆盖一些方法,例如__getitem__
等等(这直观地符合Python的鸭子类型哲学),并且让他们与我的原始流互动。但是,这似乎根本不起作用 - 我的覆盖完全被忽略了。
是否可以在纯Python中创建这样的“懒惰”string
,而不使用C扩展?如果是这样,怎么样?
忽略替代解决方案的一些背景知识:
mmap
(流内容不是文件)演示bytes
抵制修改的示例代码:
class FancyWrapper(bytes):
def __init__(self, base_str):
pass #super() isn't called and yet the code below finds abc, aaa and bbb
print(re.findall(b'[abc]{3}', FancyWrapper(b'abc aaa bbb def')))
答案 0 :(得分:2)
嗯,我发现它不可能,不是现在。
Python的re
模块在内部对字符串进行操作,因为它扫描普通的C缓冲区,这需要它接收的对象满足这些属性:
因此,即使我们设法re
使用bytes
或string
之外的其他内容,我们也必须使用mmap
- 类似的行为,即将我们的内容提供者模拟为系统内存中的线性区域。
但是mmap
机制只能 用于文件,事实上,即使这样也非常有限。例如,根据this answer,如果有人尝试写入大文件,则不能mmap
。
regex
模块,其中包含许多超级加密项,例如(?r)
,也不适用于string
和bytes
之外的内容来源。为了完整性:这是否意味着我们已经搞砸了,无法使用re
扫描大型动态内容?不必要。如果我们允许限制最大匹配大小,那么可以采用这种方法。该解决方案的灵感来自cfi的评论,并将其扩展为二进制文件。
使用两倍大的缓冲区作为最大匹配大小可以实现什么?想象一下,用户搜索A{3}
并将最大匹配大小设置为3.如果我们只读取 max match size 字节到扫描缓冲区和当前 x 包含AABBBA
:
AAB
。不配。BBA
。仍然没有比赛。这显然很糟糕,简单的解决方案是读取两倍于我们跳过的字节,以确保扫描缓冲区尾部的异常得到解决。
请注意,扫描缓冲区中第一个匹配项的短路应该可以防止其他异常情况,例如缓冲区欠压。它可能会被调整以最小化包含多个匹配的扫描缓冲区的读取,但我想避免使事情进一步复杂化。
这可能不是最高效的算法,但对我的用例来说已经足够了,所以我把它留在这里。