在Windows中是否有API可以在文件中间写入数据而不覆盖任何数据,也无需重写插入点后的所有数据?
如果可能的话,它显然会使文件碎片化;在出现严重问题之前,我能进行多少次这样的操作?
如果不可能,通常采取什么方法/解决方案?使用大型(即,千兆字节)文件时,重新编写插入点后的所有内容会很快变得难以实现。
注意:我无法避免对文件中间进行写入。请将应用程序视为用于巨大文件的文本编辑器,其中用户键入内容,然后保存。我也无法将文件拆分为几个较小的文件。
如果您需要的中间结果是可以被编辑器以外其他应用程序使用的平面文件,我不知道任何方法可以实现。 如果您希望生成一个平面文件,则必须从更改点更新到文件末尾,因为它实际上只是一个连续的文件。
但是斜体字有其存在的原因。如果您可以控制文件格式,则有一些选项。某些版本的MS Word具有快速保存功能,它们不重新编写整个文档,而是将增量记录附加到文件末尾。然后,在重新读取文件时,按顺序应用所有增量,以便得到正确的文件。如果保存的文件必须立即供另一个不了解文件格式的应用程序使用,则显然无法使用该方法。
我的建议是不要将文件存储为文本。使用中间形式进行高效编辑和保存,然后执行一步操作,将其转换为可用的文本文件,例如在编辑器退出时执行。这样,用户可以随意保存,但耗费时间的操作对性能影响不会太大。
除此之外,还有一些其他可能性。
将文件进行内存映射(而不是加载)可能提供效率,从而加快速度。您可能仍然需要重写文件末尾,但这将在操作系统中的较低级别上发生。
如果您希望快速保存的主要原因是让用户继续工作(而不是让文件可用于另一个应用程序),则可以将保存操作分配给单独的线程,并立即将控制权返回给用户。然后,您需要在两个线程之间进行同步以防止用户修改尚未保存到磁盘的数据。
类似于文件系统。
要添加新数据,请将其附加到末尾并更新索引(将id添加到列表中)。
您必须想出如何处理删除记录和更新。
如果记录大小相同,则要删除,只需标记为空,并在下次使用适当更新索引表时重用它。
如果你真的想这样做,可能最有效的方法是调用ReadFileScatter()
来读取插入点前后的块,在FILE_SEGMENT_ELEMENT[3]
列表的中间插入新数据,并调用WriteFileGather()
。是的,这涉及在磁盘上移动字节。但你把难点留给了操作系统。
MemoryMappedFile bigFile = MemoryMappedFile.CreateFromFile(
new FileStream(@"C:\bigfile.dat", FileMode.Create),
"BigFileMemMapped",
1024 * 1024,
MemoryMappedFileAccess.ReadWrite);
MemoryMappedViewAccessor view = MemoryMapped.CreateViewAccessor();
int offset = 1000000000;
view.Write<ObjectType>(offset, ref MyObject);
我注意到了paxdiablo关于处理其他应用程序的答案,以及Matteo Italia关于可安装文件系统的评论。这让我意识到还有另一个非平凡的解决方案。
使用重分析点,您可以从基本文件加上增量创建一个“虚拟”文件。任何不知道此方法的应用程序将看到一系列连续的字节,因为文件系统过滤器会即时应用增量。对于小增量(总计<16 KB),增量信息可以存储在重分析点本身中;而较大的增量则可以放置在替代数据流中。当然,这是一个非平凡的解决方案。
我知道这个问题标记为“Windows”,但是我仍然会加上我的$0.05并说,在Linux上可以在不留下空洞或将第二个半部分向前/向后复制的情况下插入或删除文件中间的一块数据:
fallocate(fd, FALLOC_FL_COLLAPSE_RANGE, offset, len)
fallocate(fd, FALLOC_FL_INSERT_RANGE, offset, len)
再次声明,我知道这可能对提问者没有帮助,但我个人是在寻找一个关于Linux的特定答案时才来到这里的。(问题中没有“Windows”一词,因此搜索引擎将我引到了这里。)