我有一个自定义的类似序列的对象 s
,它继承了 collections.Sequence
并实现了自定义的 __len__
和 __getitem__
。它代表一大串字符串 (>4GB) 并且是延迟加载的(我无法承受将所有内容加载到内存中)。
我想对其进行 RE 匹配,re.compile('some-pattern').match(s)
,但它失败并显示 TypeError: expected string or buffer
。
在实践中,pattern 不像'.*'
那样需要加载整个;通常需要前几十个字节才能匹配;但是,我无法事先告诉确切的字节数,我希望保持通用,因此我不想做类似
re.compile('some-pattern').match(s[:1000 ])
.
关于如何创建 re
接受的类似 str
的对象有什么建议吗?
以下代码说明了我不成功的尝试。从 str
继承也不起作用。
In [1]: import re, collections
In [2]: class MyStr(collections.Sequence):
def __len__(self): return len('hello')
def __getitem__(self, item): return 'hello'[item]
...:
In [3]: print(re.compile('h.*o').match(MyStr()))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-df08913b19d7> in <module>()
----> 1 print(re.compile('h.*o').match(MyStr()))
TypeError: expected string or buffer
如果字符串的大块来自单个大文件,那么我可以使用 mmap
,它应该可以工作。但是,我的情况更复杂。我有多个大文件,我 mmap
编辑了每个文件,并有一个自定义类,它是它们的串联 View 。我实际上想从 View 中的任何给定位置开始执行 RE 匹配。我在原始问题中省略了这些细节,但我认为这可能对想要了解为什么我有如此奇怪的要求的人有所帮助。
最佳答案
没有您可以实现的特殊方法会让re.match()
接受您的自定义类,而不需要您将所有数据读入内存。
那是因为目前没有特殊方法可以让您的自定义类充当 buffer-protocol object . re
方法只接受 str
字符串(实现缓冲协议(protocol))和 unicode
字符串(和子类,直接访问数据,而不是通过 __unicode__
)。 re
方法不接受任意序列,只有缓冲区协议(protocol)可以让您避免一次性将整个内容读入内存。
但是,如果您的数据完全存储在单个磁盘文件中(但由于太大而无法读入内存),与其尝试实现自定义对象,不如使用 memory mapping .内存映射使用操作系统的虚拟内存设施来访问文件的一部分作为内存部分。
虚拟内存子系统通过将内存块(“页面”)放到硬盘上,让您的操作系统管理比计算机以 RAM 形式物理可用的内存更多的内存。访问内存时,操作系统不断将页面从磁盘换出到物理内存,然后再换回来。内存映射只是将此功能扩展到现有文件,从而可以将非常大的文件视为单个大字符串,其中操作系统将确保您尝试访问的部分在需要时在内存中可用。
在 Python 中,此功能可通过 mmap
module 获得,内存映射文件实现缓冲协议(protocol)。您可以将此类对象直接传递给 re.match()
,Python 和您的操作系统将协同工作以在文件中搜索数据以进行匹配。
因此,给定一个大文件 filename = '/path/to/largefile'
和正则表达式 pattern
,这将在文件开头搜索匹配项你的模式:
import re
import mmap
import os
fd = os.open(filename, os.O_RDONLY)
mapped = mmap.mmap(fd, 0)
matched = re.match(pattern, mapped)
如果您有多个文件,您需要找到一种方法来连接它们。虚拟地,或物理地。如果您使用的是 Linux,则可以使用网络 block 设备虚拟地串联文件,或者可以使用 FUSE 虚拟文件系统。参见 A virtual file containing the concatenation of other files .
关于用于自定义序列类型的 Python re,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55562629/