纯Python3

时间:2016-02-28 11:22:17

标签: python built-in low-level

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

对于文件流,有mmap模块可以模拟字符串,因此可以re自由使用。

现在我想知道mmap如何设法制作re可以进一步重用的对象。如果我只是传递任何内容,re保护自己免于使用与TypeError: expected string or bytes-like object太不兼容的对象。所以我想我会创建一个派生自stringbytes的类,并覆盖一些方法,例如__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')))

1 个答案:

答案 0 :(得分:2)

嗯,我发现它不可能,不是现在。

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

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

    因此,即使我们设法re使用bytesstring之外的其他内容,我们也必须使用mmap - 类似的行为,即将我们的内容提供者模拟为系统内存中的线性区域。

  2. 但是mmap机制只能 用于文件,事实上,即使这样也非常有限。例如,根据this answer,如果有人尝试写入大文件,则不能mmap

  3. 即使是regex模块,其中包含许多超级加密项,例如(?r),也不适用于stringbytes之外的内容来源。
  4. 为了完整性:这是否意味着我们已经搞砸了,无法使用re扫描大型动态内容?不必要。如果我们允许限制最大匹配大小,那么可以采用这种方法。该解决方案的灵感来自cfi的评论,并将其扩展为二进制文件。

    1. n =最大匹配尺寸。
    2. x
    3. 位置开始搜索
    4. 虽然有以下内容:
      1. 导航至 x
      2. 位置
      3. 2 * n 字节读取到扫描缓冲区
      4. 在扫描缓冲区中找到第一个匹配项
      5. 如果找到匹配项:
        1. x = x + match_pos + match_size
        2. 通知 match_pos match_size
      6. 如果未找到匹配项:
        1. x = x + n
    5. 使用两倍大的缓冲区作为最大匹配大小可以实现什么?想象一下,用户搜索A{3}并将最大匹配大小设置为3.如果我们只读取 max match size 字节到扫描缓冲区和当前 x 包含AABBBA

      1. 此迭代将查看AAB。不配。
      2. 下一次迭代会将指针移动到 x + 3
      3. 现在扫描缓冲区看起来像这样:BBA。仍然没有比赛。
      4. 这显然很糟糕,简单的解决方案是读取两倍于我们跳过的字节,以确保扫描缓冲区尾部的异常得到解决。

        请注意,扫描缓冲区中第一个匹配项的短路应该可以防止其他异常情况,例如缓冲区欠压。它可能会被调整以最小化包含多个匹配的扫描缓冲区的读取,但我想避免使事情进一步复杂化。

        这可能不是最高效的算法,但对我的用例来说已经足够了,所以我把它留在这里。