在mmap文件中删除/插入数据

7 投票
2 回答
5879 浏览
提问于 2025-04-16 17:09

我正在用Python写一个脚本,目的是通过mmap()来处理一个文件。

这个任务需要我对文件的内容进行以下几种操作:

  1. 替换数据
  2. 在文件的特定位置添加数据
  3. 从文件中删除数据(不是简单地把它涂掉)

替换数据的操作很顺利,只要旧数据和新数据的字节数相同就可以:

VDATA = mmap.mmap(f.fileno(),0)
start = 10
end = 20
VDATA[start:end] = "0123456789"

但是,当我尝试删除数据(用""替换某个范围)或者插入数据(用比原来范围更长的内容替换)时,我收到了一个错误信息:

IndexError: mmap slice assignment is wrong size

这个错误是可以理解的。

现在的问题是,我该如何在mmap映射的文件中插入和删除数据呢?从文档中看,似乎我可以通过一系列低级操作来移动文件的全部内容,但如果有更简单的解决方案,我更希望能避免这样做。

2 个回答

3

没有办法在不明确操作的情况下移动文件的内容,无论这个文件是通过内存映射(mmap)还是普通方式打开的。如果是内存映射的文件,你需要使用 mmap.move 这个方法来进行移动。

10

因为没有其他选择,我写了两个辅助函数 - deleteFromMmap() 和 insertIntoMmap() - 来处理一些底层的文件操作,这样可以让开发变得更简单。

关闭和重新打开 mmap,而不是使用 resize(),是因为在 Unix 系统上 Python 有个 bug,导致 resize() 不能正常工作。(http://mail.python.org/pipermail/python-bugs-list/2003-May/017446.html)

这些函数包含在一个完整的示例中。使用全局变量是因为主项目的格式这样要求,但你可以很容易地调整它,以符合你的编码标准。

import mmap

# f contains "0000111122223333444455556666777788889999"

f = open("data","r+")
VDATA = mmap.mmap(f.fileno(),0)

def deleteFromMmap(start,end):
    global VDATA
    length = end - start
    size = len(VDATA)
    newsize = size - length

    VDATA.move(start,end,size-end)
    VDATA.flush()
    VDATA.close()
    f.truncate(newsize)
    VDATA = mmap.mmap(f.fileno(),0)

def insertIntoMmap(offset,data):
    global VDATA
    length = len(data)
    size = len(VDATA)
    newsize = size + length

    VDATA.flush()
    VDATA.close()
    f.seek(size)
    f.write("A"*length)
    f.flush()
    VDATA = mmap.mmap(f.fileno(),0)

    VDATA.move(offset+length,offset,size-offset)
    VDATA.seek(offset)
    VDATA.write(data)
    VDATA.flush()

deleteFromMmap(4,8)

# -> 000022223333444455556666777788889999

insertIntoMmap(4,"AAAA")

# -> 0000AAAA22223333444455556666777788889999

撰写回答