例如,如果我有很多数据条目存储在一个文件中,每个都有不同的大小,并且我有 1000 个条目,这使得文件像 100MB 一样大,如果我想删除文件中间的一个条目,它的大小为50KB,我怎样才能删除文件中空的 50KB 字节而不移动所有结束字节来填充它?
我正在使用诸如这些的 winapi 函数进行文件管理:
CreateFile
, WriteFile
,ReadFile
和SetFilePointerEx
例如,如果我有很多数据条目存储在一个文件中,每个都有不同的大小,并且我有 1000 个条目,这使得文件像 100MB 一样大,如果我想删除文件中间的一个条目,它的大小为50KB,我怎样才能删除文件中空的 50KB 字节而不移动所有结束字节来填充它?
我正在使用诸如这些的 winapi 函数进行文件管理:
CreateFile
, WriteFile
,ReadFile
和SetFilePointerEx
如果您真的想这样做,请在您的条目中设置一个标志。当您想从文件中删除条目时,只需使该标志(逻辑删除)无效,而无需物理删除它。下次添加条目时,只需浏览文件,查找第一个无效条目,然后覆盖它。如果所有内容都经过验证,请将其附加到末尾。假设从磁盘读取/写入单个条目是基本操作,则O(1)
删除条目和添加新条目需要时间。O(n)
您甚至可以进一步优化它。在文件的开头,存储一个位图(1
用于无效)。例如,0001000...
表示您文件中的第 4 个条目无效。1
添加条目时,在位图中搜索第一个条目并使用随机文件 I/O(与顺序文件 I/O相比)将文件指针重定向到该条目以直接覆盖。以这种方式添加只需要O(1)
时间。
哦,我注意到你的评论了。如果您想在物理删除条目的情况下有效地执行此操作,一种简单的方法是将要删除的条目与文件中的最后一个交换并删除最后一个,假设您的条目未排序。时间也不错,O(1)
既加又删。
编辑:正如乔提到的,this requires that all of your entries have the same size
. 您可以使用可变长度的条目来实现一个,但这会比这里讨论的那个更复杂。
您可以简单地继续标记未使用的空间,并且在内部碎片超过一定比例一段时间后,您可以运行一个压缩文件的例程。使用此方案,删除速度会很快,但需要进行一些定期重组。如果您有单独的文件处理方案,那么您可以将文件分成一些块,然后跟踪空闲块,并在删除时将块标记为未使用并跟踪它,然后在插入的情况下重用它. 此方案将取决于文件中的记录类型,固定或可变长度记录。
令 A = 文件开头,B = 要删除的块的开头,C = 要删除的块的结尾
CreateFile
带标志FILE_FLAG_RANDOM_ACCESS
SetFilePointerEx
到位置 C,将 EOF 读入缓冲区(考虑到您的文件大小,这可能是一个很大的缓冲区。小心巨大的记录,因为现在任何文件 IO 操作都必须分配记录大小的虚拟内存来执行任何简单的操作,例如移动)。
将缓冲区复制到文件中的位置 B
现在应该在位置 B + sizeof(block C)。调用SetEndOfFile
以在该位置截断文件,然后关闭。
请注意,使用memmove函数可以更轻松地完成此操作。但是,这需要您将整个文件映射到内存中,进行移动并将其写回。这对于小文件非常有用,但是对于大于 50-100MB 的文件,我会提醒您注意有足够的可用连续虚拟地址空间。