Python3中的类似Mmap的行为

2024-09-30 16:22:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我希望使用re模块与流,但不一定是文件流,以最小的开发成本。在

对于文件流,有一个mmap模块,它能够模拟字符串,因此可以与re一起自由使用。在

现在我想知道mmap如何设计一个re可以进一步重用的对象。如果我只传递任何内容,re保护自己不受与TypeError: expected string or bytes-like object不兼容的对象的使用。所以我想我应该创建一个从stringbytes派生的类,并覆盖一些方法,比如__getitem__等(这很直观地符合Python的duck类型哲学),并使它们与我的原始流交互。然而,这似乎根本不起作用-我的覆盖被完全忽略了。在

有没有可能在纯Python中创建这样一个“lazy”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')))

Tags: 模块and文件对象字符串re内容string
1条回答
网友
1楼 · 发布于 2024-09-30 16:22:45

嗯,我发现这不可能,目前不行。在

  1. Python的re模块对字符串进行内部操作,因为它扫描的是一个普通的C缓冲区,它需要接收到的对象来满足这些属性:

    • 它们的表示必须驻留在系统内存中
    • 它们的表示必须是线性的,例如不能包含任何类型的间隙
    • 它们的表示必须包含我们作为一个整体搜索的内容。在

    因此,即使我们设法使rebytes或{}不同的东西工作,我们也必须使用类似mmap的行为,即将我们的内容提供者模拟为系统内存中的线性区域。

  2. 但是mmap机制只对文件有效,事实上,即使这样也很有限。例如,如果一个人试图写入一个大文件,就不能mmap它,就像this answer

  3. 即使包含许多超级重复添加的regex模块,也不能容纳string和{}之外的内容源。在

完整性方面:这是否意味着我们已经完蛋了,不能用re来浏览大量动态内容?不一定。有一种方法可以做到,如果我们允许限制最大匹配大小。该解决方案的灵感来自cfi的注释,并将其扩展到二进制文件。在

  1. n=最大匹配大小。在
  2. 开始搜索位置x
  3. 有内容的时候:
    1. 导航到位置x
    2. 读取2*n字节以扫描缓冲区
    3. 在扫描缓冲区内查找第一个匹配项
    4. 如果找到匹配项:
      1. x=x+匹配位置+匹配尺寸
      2. 通知匹配位置匹配大小
    5. 如果未找到匹配项:
      1. x=x+n

通过使用两倍于最大匹配大小的缓冲区来实现这一点?假设用户搜索A{3},并且最大匹配大小设置为3。如果我们只读取max match size字节到扫描缓冲区,并且当前x的数据包含AABBBA

  1. 此迭代将查看AAB。不匹配。在
  2. 下一次迭代将指针移动到x+3。在
  3. 现在扫描缓冲区如下所示:BBA。还是没有对手。在

这显然很糟糕,简单的解决方案是读取两倍于我们跳过的字节数,以确保扫描缓冲区尾部附近的异常得到解决。在

注意,在扫描缓冲区内的第一个匹配上的短路被认为是为了防止其他异常,例如缓冲区扫描不足。可能会对其进行调整,以最小化包含多个匹配项的扫描缓冲区的读取,但我希望避免使事情进一步复杂化。在

这可能不是最具性能的算法,但对于我的用例来说已经足够好了,所以我将它留在这里。在

相关问题 更多 >