如何从内存映射文件中读取行?

25 投票
5 回答
30108 浏览
提问于 2025-04-17 06:26

看起来mmap这个接口只支持readline()方法。
如果我尝试遍历这个对象,我得到的是单个字符,而不是完整的行。

那么,使用“python风格”的方法逐行读取一个mmap文件应该怎么做呢?

import sys
import mmap
import os


if (len(sys.argv) > 1):
  STAT_FILE=sys.argv[1]
  print STAT_FILE
else:
  print "Need to know <statistics file name path>"
  sys.exit(1)


with open(STAT_FILE, "r") as f:
  map = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
  for line in map:
    print line # RETURNS single characters instead of whole line

5 个回答

1

下面的内容比较简洁:

with open(STAT_FILE, "r") as f:
    m = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
    while True:
        line = m.readline()  
        if line == "": break
        print line
    m.close()

注意,line会保留换行符,所以你可能想把它去掉。这也是为什么if line == ""能正常工作的原因(空行会被返回为"\n")。

原始的迭代之所以这样工作,是因为mmap试图同时表现得像一个文件和一个字符串。在迭代的时候,它看起来像一个字符串。

我不知道为什么它不能(或者选择不)提供readlines()/xreadlines()这个功能。

15

我把你的例子改成这样:

with open(STAT_FILE, "r+b") as f:
        m=mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
        while True:
                line=m.readline()
                if line == '': break
                print line.rstrip()

建议:

  • 不要把变量命名为 map,因为这是一个内置函数的名字。
  • r+b模式打开文件,就像在 mmap 帮助页面的Python例子中提到的。它说:在任何情况下,你必须提供一个用于更新的文件的文件描述符。可以查看 http://docs.python.org/library/mmap.html#mmap.mmap
  • 尽量不要使用 UPPER_CASE_WITH_UNDERSCORES 这种全大写的全局变量名,具体可以参考 全局变量名称https://www.python.org/dev/peps/pep-0008/#global-variable-names。在其他编程语言(比如C语言)中,常量通常都是全大写的。

希望这些建议对你有帮助。

编辑:我在Linux上做了一些时间测试,因为评论让我很好奇。这里是对一个137MB文本文件进行5次连续运行的时间比较。

正常文件访问:

real    2.410 2.414 2.428 2.478 2.490
sys     0.052 0.052 0.064 0.080 0.152
user    2.232 2.276 2.292 2.304 2.320

mmap 文件访问:

real    1.885 1.899 1.925 1.940 1.954
sys     0.088 0.108 0.108 0.116 0.120
user    1.696 1.732 1.736 1.744 1.752

这些时间测试不包括 print 语句(我把它排除了)。根据这些数字来看,内存映射文件的访问速度要快很多。

编辑 2:使用 python -m cProfile test.py 我得到了以下结果:

5432833    2.273    0.000    2.273    0.000 {method 'readline' of 'file' objects}
5432833    1.451    0.000    1.451    0.000 {method 'readline' of 'mmap.mmap' objects}

如果我没记错的话,mmap 的速度确实快很多。

另外,似乎 not len(line) 的性能比 line == '' 差,至少这是我对分析器输出的理解。

36

遍历一个 mmap 的每一行,最简洁的方法是:

with open(STAT_FILE, "r+b") as f:
    map_file = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
    for line in iter(map_file.readline, b""):
        # whatever

需要注意的是,在 Python 3 中,iter() 的哨兵参数必须是 bytes 类型,而在 Python 2 中,它需要是 str 类型(也就是用 "" 而不是 b"")。

撰写回答