使用mmap对整个文件应用正则表达式

4 投票
1 回答
7149 浏览
提问于 2025-04-18 17:01

我正在尝试对整个文件应用正则表达式(而不是单独处理每一行),使用的代码如下:

import mmap, re

ifile = open(ifilename)
data = mmap.mmap(ifile.fileno(), 0)
print data
mo = re.search('error: (.*)', data)
if mo:
    print "found error"

这个方法是基于一个问题的回答,链接在这里:如何在不将整个文件读入内存的情况下进行 re.search 或 re.match?

但是我遇到了以下错误:

Traceback (most recent call last):
  File "./myscript.py", line 29, in ?
    mo = re.search('error: (.*)', data)
  File "/usr/lib/python2.3/sre.py", line 137, in search
    return _compile(pattern, flags).search(string)
TypeError: expected string or buffer

我该如何解决这个问题呢?


在另一个问题中,我发现了另一种读取整个文件的方法,而不是使用 mmap 对象,链接在这里:在文件对象中匹配多行正则表达式

data = open("data.txt").read()

有没有什么理由更喜欢 mmap 而不是简单的缓冲区/字符串呢?

1 个回答

10

其实你这里面有两个问题。

你的技术问题

你遇到的问题很可能通过升级到更新版本的Python来解决,或者至少可以得到更好的错误追踪信息。mmap文档说明你需要以更新的方式打开文件才能使用mmap,而你现在并没有这样做。

ifile = open(ifilename) # default is to open as read

应该是这样的:

ifile = open(ifilename, 'r+')

或者,如果你能像你评论中提到的那样更新到Python 2.6,

with open(ifilename, 'r+') as fi:
    # do stuff with open file

如果你在2.7中没有以写权限打开文件并尝试使用mmap,会出现“权限被拒绝”的错误。我怀疑这个错误在2.3中没有实现,所以现在你被允许继续使用一个无效的mmap对象,而当你尝试用正则表达式搜索它时就会失败。

mmap与open().read()

最终,你可以用这两种方法做(几乎)相同的事情。re.search(pattern, mmap_or_long_string)可以在你的内存映射文件或从read()调用得到的长字符串中进行搜索。

这两种方法的主要区别在于虚拟内存和实际内存的使用。内存映射文件保留在磁盘上(或其他地方),你通过虚拟内存地址直接访问它。而使用read()时,你是一次性将整个文件读入(实际)内存。

为什么选择其中一种:

  1. 文件大小
    你可以映射的文件大小的最大限制是你的虚拟内存地址空间的大小,这取决于你的CPU(32位或64位)。分配的内存必须是连续的,因此如果操作系统找不到足够大的内存块来分配,就可能会出现分配错误。而使用read()时,你的限制是可用的物理内存。如果你要访问的文件比可用内存大,并且逐行读取又不是一个选项,考虑使用mmap。

  2. 进程间文件共享
    如果你在对一个大文件进行并行的只读操作,你可以将其映射到内存中,以便在进程之间共享,而不是每个进程都读取整个文件的副本。

  3. 可读性/熟悉度
    很多人对简单的open()read()函数更熟悉,而不是内存映射。除非你有很强的理由使用mmap,否则长期来看,使用基本的输入输出函数可能更好,便于维护。

  4. 速度
    这一点就比较复杂了。很多论坛和帖子喜欢讨论mmap的速度(因为它在文件映射后可以绕过一些系统调用),但底层机制仍然是在访问磁盘,而一次性读取整个文件则是在开始和结束时才进行磁盘访问。如果你试图考虑缓存(硬盘和CPU)、内存分页和文件访问模式,会变得非常复杂。坚持使用经过验证的方法进行性能分析会更简单。你根据你的具体使用情况和文件访问模式看到不同的结果,所以最好对两者进行性能分析,看看哪种对你来说更快。

其他资源

关于差异的好总结
PyMOTW
一个好的SO问题
维基百科虚拟内存文章

撰写回答