7.10. mmap — 内存映射模块

未匹配的标注

目的:使用内存映射文件而不是直接读取内容。

内存映射文件使用操作系统虚拟内存直接访问文件上的数据,而不是通过常规的 I/O 方法。内存映射通常可以提高 I/O 性能,因为对于每次访问它不涉及单独的系统调用,也不会在缓冲池之间复制数据,而是内核和用户程序可以直接访问内存。

内存映射文件可视为可变字符串或类文件对象,取决于具体需要。一个被映射的文件支持文件 API 方法,例如, close()flush()read()readline()seek()tell()write()。它还支持字符串 API,并具有诸如切片和 find() 方法的功能。

所有的例子都将使用文本文件 lorem.txt,包含一些假文本,例如:

lorem.txt

Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Donec egestas, enim et consectetuer ullamcorper, lectus ligula rutrum leo,
a elementum elit tortor eu quam. Duis tincidunt nisi ut ante. Nulla
facilisi. Sed tristique eros eu libero. Pellentesque vel
arcu. Vivamus purus orci, iaculis ac, suscipit sit amet, pulvinar eu,
lacus. Praesent placerat tortor sed nisl. Nunc blandit diam egestas
dui. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Aliquam viverra fringilla
leo. Nulla feugiat augue eleifend nulla. Vivamus mauris. Vivamus sed
mauris in nibh placerat egestas. Suspendisse potenti. Mauris
massa. Ut eget velit auctor tortor blandit sollicitudin. Suspendisse
imperdiet justo.

提醒

Unix 和 Windows 之间 mmap() 的参数和行为有所不同,这里不会完全讨论,详细内容请看标准库文档。

使用 mmap() 创建一个内存映射文件。第一个参数是一个文件描述符,要么是来自 file 对象的 fileno() 方法, 要么来自 os.open()。使用者负责在调用 mmap() 之前打开文件,并且在使用结束之后关闭它。

第二个参数传入 mmap() 的参数是要去映射的文件内容的大小。如果值是 0 ,那么代表映射整个文件。如果这个只存超过当前文件,文件将会被扩展。

注意

Windows 不支持零长度映射文件。

可选的参数 access 在两个平台都是支持的,使用 ACCESS_READ 表示只读,ACCESS_WRITE 表示直接写(对内存的操作直接写入文件),或者 ACCESS_COPY 用于写时复制(内存分配不写入文件)。

mmap_read.py

import mmap

with open('lorem.txt', 'r') as f:
    with mmap.mmap(f.fileno(), 0,
                   access=mmap.ACCESS_READ) as m:
        print('First 10 bytes via read :', m.read(10))
        print('First 10 bytes via slice:', m[:10])
        print('2nd   10 bytes via read :', m.read(10))

文件指针会追踪切片操作上次读取的位置。这个例子中,第一次读取之后指针向前移动了 10 字节。在切片操作开始之前,文件指针重置到文件开始处,然后又向前移动了 10 字节。切片操作之后,调用 read(10) 将会得到文件 11-20 字节的内容。

$ python3 mmap_read.py

First 10 bytes via read : b'Lorem ipsu'
First 10 bytes via slice: b'Lorem ipsu'
2nd   10 bytes via read : b'm dolor si'

为了设置一个内存映射文件去接受更新,要以追加模式 r+ (而不是 w)打开然后再进行映射。然后可以使用任何更新数据的 API(write(),赋值到切片等方式)。

下一个例子使用了默认的访问模式 ACCESS_WRITE,然后通过将值赋给切片的方式修改文件的部分行。

mmap_write_slice.py

import mmap
import shutil

# 复制示例文件
shutil.copyfile('lorem.txt', 'lorem_copy.txt')

word = b'consectetuer'
reversed = word[::-1]
print('Looking for    :', word)
print('Replacing with :', reversed)

with open('lorem_copy.txt', 'r+') as f:
    with mmap.mmap(f.fileno(), 0) as m:
        print('Before:\n{}'.format(m.readline().rstrip()))
        m.seek(0)  # rewind

        loc = m.find(word)
        m[loc:loc + len(word)] = reversed
        m.flush()

        m.seek(0)  # rewind
        print('After :\n{}'.format(m.readline().rstrip()))

        f.seek(0)  # rewind
        print('File  :\n{}'.format(f.readline().rstrip()))

内存和文件中第一行中间部分的 「consectetuer」将被替换。

$ python3 mmap_write_slice.py

Looking for    : b'consectetuer'
Replacing with : b'reutetcesnoc'
Before:
b'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.'
After :
b'Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit.'
File  :
Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit.

复制模式

使用模式 ACCESS_COPY 访问文件,将不会将更新内容写入到磁盘文件。

mmap_write_copy.py

import mmap
import shutil

# 复制示例文件
shutil.copyfile('lorem.txt', 'lorem_copy.txt')

word = b'consectetuer'
reversed = word[::-1]

with open('lorem_copy.txt', 'r+') as f:
    with mmap.mmap(f.fileno(), 0,
                   access=mmap.ACCESS_COPY) as m:
        print('Memory Before:\n{}'.format(
            m.readline().rstrip()))
        print('File Before  :\n{}\n'.format(
            f.readline().rstrip()))

        m.seek(0)  # rewind
        loc = m.find(word)
        m[loc:loc + len(word)] = reversed

        m.seek(0)  # rewind
        print('Memory After :\n{}'.format(
            m.readline().rstrip()))

        f.seek(0)
        print('File After   :\n{}'.format(
            f.readline().rstrip()))

这个例子中将文件句柄和 mmap 句柄分开是必要的,因为两个对象的内部状态是单独维护的。

$ python3 mmap_write_copy.py

Memory Before:
b'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.'
File Before  :
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

Memory After :
b'Lorem ipsum dolor sit amet, reutetcesnoc adipiscing elit.'
File After   :
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.

正则表达式

因为一个内存映射文件拥有一个字符串的行为,因此可以使用其他操作字符串的模块(如正则表达式)一起使用。这个例子中将会找到所有以「nulla」开始的句子。

mmap_regex.py

import mmap
import re

pattern = re.compile(rb'(\.\W+)?([^.]?nulla[^.]*?\.)',
                     re.DOTALL | re.IGNORECASE | re.MULTILINE)

with open('lorem.txt', 'r') as f:
    with mmap.mmap(f.fileno(), 0,
                   access=mmap.ACCESS_READ) as m:
        for match in pattern.findall(m):
            print(match[1].replace(b'\n', b' '))

因为这个模式包含两个分组,findall() 的返回值是一个元组列表。print 语句读取匹配的句子并且将行末换行符替换为空格,这样让每个结果都在一行里。

$ python3 mmap_regex.py

b'Nulla facilisi.'
b'Nulla feugiat augue eleifend nulla.'

推荐阅读

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/pymotw/mmap-mem...

译文地址:https://learnku.com/docs/pymotw/mmap-mem...

上一篇 下一篇
贡献者:2
讨论数量: 0
发起讨论 只看当前版本


暂无话题~