1. mmap内存映射文件
创建一个文件的内存映射, 会运用操作系统虚拟内存去直接访问文件系统里的数据, 并非运用常规的I/O函数获取数据。内存映射一般能够给予I/O性能, 鉴于使用内存映射时, 用不着为每个访问构建一个独立的系统调用, 也无需在缓冲区之间拷贝数据;事实上, 内核以及用户应用均可直接访问内存。
内存映射文件, 可被视作可修改的字符串, 或者类似文件的对象, 这要依据具体的需求来定。映射文件, 对一般的文件API方法予以支持, 像close()、flush()、read()、()、seek()、tell()以及write()。它同样支持字符串API, 能提供类似分片这样的特性, 还有类似find()的方法。
下述所有示例, 均会选用文本文件lorem.txt, 该文件之中存有一些Lorem Ipsum。为方便参考起见, 以下代码清单呈现出此文件的文本内容。
坐在那里的人身上有洛伦 ipsum 这个东西, 是痛苦的, 精英人士。
Donec , enim et , leo,
. Sed eros eu . vel
弯弯的, 纯净的兽穴, 并且, 处于, 在……之中, 和……同时。
湖泊, 然而却是草皮。现在, 那片区域, 直径。
dui. morbi et netus et
fames ac .
Leo, 没有增大的那个没有, 然而, 有沉淀。
in nibh . .
massa. Ut eget velit .
justo.
1.1 读文件
运用mmap()函数能够创建出一个内存映射文件, 第一个参数乃是文件描述符, 其有可能源自file对象的()方法, 又或许源自os.open(), 而调用者在调用mmap()之前需承担起打开文件的职责, 在不再需要该文件之时要履行将其关闭的责任。
mmap()的第二个参数所指示的, 是那要进行映射的文件部分的大小, 此大小用字节作为单位来衡量。要是这个值为0, 那么便会映射整个文件。倘若所指示的这个大小, 比文件的当前大小还要大, 那就会对该文件进行扩展。
这两个平台均支持一个可选择的关键字参数, 使用它意味着只读访问, 它还表示“写通过”, 也就是对内存的赋值会直接写入文件, 它又表示“写时复制”,此时对内存的赋值不会写至文件。
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(), 便会给出文件的11至20字节。
1.2 写文件
存在这样一种情况, 即需要去构建内存映射文件, 以此来接纳更新, 在进行映射之前, 首先得运用模式'r+'(而非'w')来打开文件, 目的是最终完成追加, 随后能够采用任何一种会对数据产生改变的API方法(像是write()或者赋值到一个分片等诸如此类的方法)。
底下的示例运用了默认访问样式, 且赋予了一个分片, 从而在原地对某一行的一部分实施修改。
import mmap
import shutil
# Copy the example file
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()))
内存的文件中第一行中间的单词“”将被替换。

使用访问设置时不会把修改写入磁盘上的文件。
import mmap
import shutil
# Copy the example file
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句柄, 缘由在于这两个对象的内部状态是会单独予以维护的。

1.3 正则表达式
鉴于内存映射文件跟一个字符串相类似, 所以它也常常会同其他处理字符串的模块一块儿被使用, 像正则表达式。下面的这个例子会将所有含有“nulla”的句子给找出来。
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' '))
因为鉴于这个模式涵盖着两个组, 因而()的返回值是一系列的元组。print语句将寻觅到相匹配的句子, 并且用以空格替换换行符之方式, 致使各个结果均打印于同一行之上。

314

被折叠的 条评论
为什么被折叠?



