例如,如果我有很多数据条目存储在一个文件中,每个都有不同的大小,我有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)
。
编辑:就像Joe提到的,this requires that all of your entries have the same size
。您可以使用可变长度的条目来实现,但这将比这里讨论的更复杂。
设A =文件的开始,B =要删除的块的开始,C =要删除的块的结束
CreateFile
with flag FILE_FLAG_RANDOM_ACCESS
SetFilePointerEx
到位置C,读到EOF到缓冲区(考虑到你的文件大小,这可能是一个很大的缓冲区)。对于庞大的记录要小心,因为任何File IO操作现在都必须分配记录大小的虚拟内存来执行任何简单的操作(如move)。
将缓冲区复制到文件
中的位置B现在应该在位置B + sizeof(block C).调用SetEndOfFile
在该位置截断文件,然后关闭。
请注意,使用memmove函数可以更容易地完成此操作。但是,这需要您将整个文件映射到内存中,进行移动,然后将其写回来。这对于小文件来说非常好,但是对于大于50-100MB的文件,我要提醒您要有足够的可用连续虚拟地址空间。
您可以简单地继续标记未使用的空间,并且在一段时间后,当内部碎片超过一定比例时,您可以运行一个例程来压缩文件。使用这种方案,清除将是快速的,但需要一些周期性的重组。如果您有一个单独的文件处理方案,那么您可以将文件划分为一些块,然后跟踪空闲块,并在删除时将块标记为未使用并跟踪它,然后在插入的情况下重用它。此方案将取决于文件中记录的类型,固定或可变长度记录。