python cookbook 学习笔记 第五章 文件与IO (10) 内存映射的二进制文件

  • 内存映射的二进制文件
  • 问题:
    • 想内存映射一个二进制文件到一个可变字节数组中,目的可能是为了随机访问它的内容或原地做些修改。
  • 解决方案:
    • 使用 mmap 模块来内存映射文件。下面是一个工具函数,演示了如何打开一个文件并以一种便捷方式内存映射这个文件。
import os
import mmap

def memory_map(filename, access= mmap.ACCESS_WRITE):

    size = os.path.getsize(filename)
    fd = os.open(filename, os.O_RDWR)
    return mmap.mmap(fd, size, access= access)
  • 为了使用这个函数,需要有一个已经创建并且内容不为空的文件。下面这个例子,是这样初始创建一个文件,并将内容 扩充到指定大小:
size = 1000000
with open("data", "wb") as f:
    f.seek(size - 1)
    f.write(b"x00")
  • 下面是一个利用 memory_map()函数内存映射文件内容的例子:
m = memory_map("data")
print(len(m)) # 1000002 (书上这里是1000000)

print(m[0:10]) # b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

m[0:11] = b"hello world"
print(m[0:11]) # b'hello world'
m.close()

with open("data", "rb") as f:
    print(f.read(11)) # b'hello world'
  • mmap()返回的 mmap 对象同业可以作为一个上下文管理器来使用,这时候底层的文件会被自动关闭。比如:
with memory_map("data") as m:
    print(len(m))  # 1000002
    print(m[0:10]) # b'hello worl'
    print(m.closed) # False

print(m.closed) # True
  • 默认情况下,memory_map()函数(原书这里应该印刷错误)打开的文件同时支持读写操作。任何的修改内容都会复制 到原始文件中,可以使用 mmap.ACCESS_COPY:
m = memory_map(filename, mmap.ACCESS_COPY)
  • 讨论: 为了随机访问文件的内容,使用 mmap 将文件映射到内存中是一个高效和优雅的方法。例如,无须打开一个文 件并执行大量的 seek(), read(), write()调用,只需要简单的映射文件并使用切片操作访问数据即可。 一般来讲, mmap()所暴露的内存看上去就是一个二进制数组对象。但是你可以使用一个内存视图来解析其中的数据, 比如:
m = memory_map("data")

v = memoryview(m).cast("I")

print(v[0]) # 7

# 这个没有测试通过
TypeError: memoryview: length is not a multiple of itemsize
  • 需要强调的一点是,内存映射一个文件并不会导致整个文件被读取到内存中,也就是说,文件并没有被复制到内存缓存 或数组中。相反,操作系统仅仅为文件内容保留了一段虚拟内存。当访问文件不同的区域时,这些区域的内容才根据需 要被读取并映射到内存区域。而那些从来没被访问过的部分还是留在磁盘上。所以这些过程是透明的,在幕后完成。
  • 如果多个 Python 解释器内存映射同一个文件,得到的 mmap 对象能够被用来在解释器直接交换数据。也就是说 所有解释器都能同时读写数据,并且其中一个解释器所做的修改会自动呈现在其他解释器中。很明显,这里需要考虑同 步的问题。但是这种方法有时候可以用来在管道或者套接字间传递数据。
  • 这一节中函数尽量写的通用,同时适用 Unix 和 windows 平台。要注意的是使用 mmap()函数时会再底层有一些平 台差异性。另外,还有一些选项可以用来创建匿名的内存映射区域。

猜你喜欢

转载自blog.csdn.net/qq_43539055/article/details/84889173