前言
如果现在有一个需求,我们需要处理一个20G的大文件,我们会怎么处理呢?我们先暂定下来自己思考下,我们需要怎么实现这个功能。
------------------------------------------------------------------------放上我的老婆大人------------------------------------------------------------------------
我们可能会这么实现
def get_datas():
source_text_path = "路径"
with open(source_text_path, 'rb') as f:
data = f.readlines()
yield data
if __name__ == '__main__':
for e in get_datas():
deal_data(e) # 处理数据
这样虽然能实现,但是我们处理的时候需要消耗的资源和性能不是很友好,所以我们要优化,接下来我们介绍下我们今天的主角----mmap
mmap概念
mmap是一种虚拟内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。它省掉了内核态和用户态页copy这个动作(两态间copy),直接将用户态的虚拟地址与内核态空间进行mapping,进程直接读取内核空间,速度提高了,内存占用也少了。
mmap介绍
windows
class mmap.mmap(fileno, length, tagname=None, access=ACCESS_DEFAULT[, offset]):
pass
参数说明:
fileno:文件描述符,可以是file对象的fileno()方法,或者来自os.open(),在调用mmap()之前打开文件,不再需要文件时要关闭。
length:要映射文件部分的大小(以字节为单位),这个值为0,则映射整个文件,如果大小大于文件当前大小,则扩展这个文件。
tagname:为映射提供标签名称的字符串,Windows 允许你对同一文件拥有许多不同的映射。如果指定现有标签的名称,则会打开该标签,否则将创建该名称的新标签。如果省略此参数或设置为 None
,则创建的映射不带名称。避免使用 tag 参数将有助于使代码在Unix和Windows之间可移植。
access: 文件权限 1- ACCESS_READ:读访问; 2- ACCESS_WRITE:写访问,默认; 3-ACCESS_COPY:拷贝访问,不会把更改写入到文件,使用flush把更改写到文件。
offset:非负整数偏移量,默认从0开始
Unix
class mmap.mmap(fileno, length, flags=MAP_SHARED, prot=PROT_WRITE|PROT_READ, access=ACCESS_DEFAULT[, offset]):
pass
参数说明: (相同可以参考上面的)
flags:映射的性质,默认 MAP_SHARED
。。 1- MAP_PRIVATE
会创建私有的写入时拷贝映射,因此对 mmap 对象内容的修改将为该进程所私有; 2- MAP_SHARED
会创建与其他映射同一文件区域的进程所共享的映射。
prot:它将给出所需的内存保护方式;最有用的两个值是 PROT_READ
和 PROT_WRITE
,分别指明页面为可读或可写。 prot 默认为 PROT_READ | PROT_WRITE。
access:注意的是可以指定 access 作为替代 flags 和 prot 的可选关键字形参。 同时指定 flags, prot 和 access 将导致错误。
fileno文件描述符有如下
os.O_RDONLY # 以只读的方式打开 Read only
os.O_WRONLY # 以只写的方式打开 Write only
os.O_RDWR # 以读写的方式打开 Read and write
os.O_APPEND # 以追加的方式打开
os.O_CREAT # 创建并打开一个新文件
os.O_EXCL # os.O_CREAT| os.O_EXCL 如果指定的文件存在,返回错误
os.O_TRUNC # 打开一个文件并截断它的长度为零(必须有写权限)
os.O_BINARY # 以二进制模式打开文件(不转换)
os.O_NOINHERIT # 阻止创建一个共享的文件描述符
os.O_SHORT_LIVED
os.O_TEMPORARY # 与O_CREAT一起创建临时文件
os.O_RANDOM # 缓存优化,但不限制从磁盘中随机存取
os.O_SEQUENTIAL # 缓存优化,但不限制从磁盘中序列存取
os.O_TEXT # 以文本的模式打开文件(转换)
支持的方法
close
(): 关闭 mmap。 后续调用该对象的其他方法将导致引发 ValueError 异常。 此方法将不会关闭打开的文件。
closed:
如果文件已关闭则返回 True
。(3.2 后支持)
find
(str, start, end): 从 start 下标开始,在 m 中从左往右寻找子串 str 最早出现的下标;没有找到则返回-1
flush([offset, n]):将对文件的内存副本的修改刷新至磁盘。 如果不使用此调用则无法保证在对象被销毁前将修改写回存储。 如果指定了 offset 和 size,则只将对指定范围内字节的修改刷新至磁盘;在其他情况下,映射的全部范围都会被刷新。windows: 返回的非零值表示成功;否则返回0。 零表示失败。 unix: 返回零值以表示成功。 当调用失败时将引发异常。
move(dest, src, count): 将从偏移量 src 开始的 count 个字节拷贝到目标索引号 dest。 如果 mmap 创建时设置了 ACCESS_READ
,则调用 move 将引发异常。
read
([n]): 返回一个字节,其中包含从当前文件位置开始的至多 n 个字节。 如果参数省略,为 None
或负数,则返回从当前文件位置开始直至映射结尾的所有字节。 文件位置会被更新为返回字节数据之后的位置
read_byte
():返回一个1字节长的字符串,从 m 对应的文件中读1个字节,要是已经到了EOF还调用 read_byte(),则抛出异常 ValueError。
readline
():返回一个字符串,从 m 对应文件的当前位置到下一个'\n',当调用 readline() 时文件位于 EOF,则返回空字符串。
resize
(newsize):如果存在的话, 改变映射以及下层文件的大小。 如果 mmap 创建时设置了 ACCESS_READ
或 ACCESS_COPY
,则改变映射大小将引发异常。
rfind
(sub[, start[, end]]):返回子序列 sub 在对象内被找到的最大索引号,使得 sub 被包含在 [start, end] 范围中。 可选参数 start 和 end 会被解读为切片表示法。 如果未找到则返回 -1
。
seek
(pos[, whence]):设置文件的当前位置。 whence 参数为可选项并且默认为 os.SEEK_SET
或 0
(绝对文件定位);其他值还有 os.SEEK_CUR
或 1
(相对当前位置查找) 和 os.SEEK_END
或 2
(相对文件末尾查找)。
size
():返回文件的长度,该数值可以大于内存映射区域的大小。
tell
():返回文件指针的当前位置。
write
(str):将str写入文件指针当前位置的内存并返回写入的字节总数 (一定不小于 len(str)
,因为如果写入失败,将会引发错误)。 在字节数据被写入后文件位置将会更新。 如果 mmap 创建时设置了 ACCESS_READ
,则向其写入将引发 异常
write_byte
(byte):将整数值 byte 写入文件指针当前位置的内存;文件位置前进 1
。 如果 mmap 创建时设置了 ACCESS_READ
,则向其写入将引发异常。
使用mmap读取大文件
from mmap import mmap
def read_data(file_path):
with open(file_path, "r+") as f:
m = mmap(f.fileno(), 0)
g_index = 0
for index, char in enumerate(m):
if char == b"\n":
yield m[g_index:index + 1].decode()
g_index = index + 1
if __name__ == "__main__":
file_path = ""
for content in read_data(file_path):
print(content)