写入到文件中间(不覆盖数据)

问题描述:

在Windows中,是否可以通过API写入文件的中间而不覆盖任何数据,并且之后不必重写所有内容?写入到文件中间(不覆盖数据)

如果可能的话,我相信它会明显地分割文件;在它成为严重问题之前,我可以做多少次?

如果不可能采取什么方法/解决方法?在插入点之后重写所有内容对于大型(即千兆字节)文件来说非常快速。


注意:我不能避免写中间。把应用程序想象成一个文本编辑器,用于在用户键入内容然后保存的大文件。我也无法将文件分成几个小文件。

如果您需要的临时结果是一个可以由编辑器以外的其他应用程序使用的平面文件,我不知道有任何方法可以做到这一点。如果您想要生成平面文件,则必须将其从更改点更新到文件末尾,因为它实际上只是一个顺序文件。

但斜体是有充分的理由。如果你可以控制文件格式,你有一些选择。某些版本的MS Word具有快速保存功能,它们不重写整个文档,而是将增量记录附加到文件末尾。然后,在重新读取文件时,它会按顺序应用所有的增量,以便您最终得到的是正确的文件。如果保存的文件必须立即可用于其他不理解文件格式的应用程序,这显然不起作用。

什么我提议存在于商店文件为文本。使用可以高效编辑和保存的中间表单,然后执行将其转换为可用文本文件的步骤(例如,在编辑器退出时)。这样,用户可以尽可能多地保存,但耗时的操作不会产生太大的影响。

除此之外,还有一些其他的可能性。

内存映射(而不是加载)文件可能提供的效率会加快速度。你可能仍然需要重写到文件的末尾,但是它会发生在操作系统的较低级别。

如果您希望快速保存的主要原因是让用户继续工作(而不是让该文件可用于其他应用程序),则可以将保存操作转出到单独的线程并立即将控制权返回给用户。然后,您需要在两个线程之间进行同步,以防止用户修改数据并将其保存到磁盘。

+1

+1用于存储器映射;但是,对于快速保存的文档文档等格式要小心:最终你会得到一个充满旧数据的巨大文件。这可能是一个问题,因为(1)浪费磁盘空间和(2)用户认为删除的数据仍然存在,因此显然为空的文件可能仍包含敏感信息。IIRC针对这些动机中的一个最新版本的Office(可能是2003,但我不确定)默认情况下,Microsoft会关闭快速保存功能:磁盘比以前快得多,这种技术的缺点超过了优势。 – 2010-03-08 10:55:12

+0

我认为从内存Word有一个门槛超过它将写入真实文件,而不是另一个三角洲,这将解决第一个问题。但是你对敏感数据是正确的,我在文档中看到过一些并不意味着被看到的东西:-) – paxdiablo 2010-03-08 10:58:11

我不确定你的文件的格式,但你可以使它'记录'的基础。

  • 将您的数据写入区块并给每个区块一个id。
  • Id可能是文件中的数据偏移量。
  • 在文件的开始处,您可以使用 包含一个包含ID列表的标头,因此您可以按照 的顺序读取记录 。
  • 在的“ID列表”结束时,你可以点到另一个位置的文件中(和ID /偏移)存储IDS

类似的东西到文件系统的另一份名单。

要添加新数据,请在最后附加它们并更新索引(将id添加到列表中)。

你必须弄清楚如何处理删除记录和更新。

如果记录的大小相同,那么要删除,可以将其标记为空,然后再次将其重新用于索引表的适当更新。

如果使用.NET 4,如果您有类似编辑器的应用程序,请尝试使用内存映射文件 - 可能只是票证。像这样的东西(我没有把它输入到VS所以不知道,如果我得到的语法正确):

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); 

的现实的答案是否定的。你唯一真正的选择是从修改的角度重写,或者建立一个更复杂的格式,使用类似索引的东西来告诉如何将记录按照其预定顺序排列。

从纯理论的角度来看,你可以在恰当的环境下做到这一点。使用FAT(例如,但大多数其他文件系统至少具有某种程度的相似性),则可以直接操作FAT。 FAT基本上是组成文件的链接列表。您可以修改该链接列表以在文件中间添加新的群集,然后将新数据写入您添加的群集。

请注意,我说纯粹的理论。在像MS-DOS这样一个完全不受保护的系统下进行这种操作本来就很困难,但却是合理的。对于大多数较新的系统,进行修改通常都很困难。大多数现代文件系统(相当)比FAT更复杂,这会增加执行的难度。从理论上讲,它仍然是可能的 - 事实上,现在它已经彻底失神了,甚至有可能在几乎合理。

+0

现代操作系统中的直接文件系统修改是braindead:你必须了解几个文件系统是如何工作的(相当困难的事情),为您的驱动程序编写您需要的扩展功能,而IFS驱动程序对于“普通”驱动程序编写者来说也是黑魔法;此外,你会将你的应用程序绑定到几个文件系统。所有这些对于性能改进往往是微不足道的。 顺便说一句,如果中间插入的文本不是集群的大小,那么根本没有性能优势。 – 2010-03-08 11:03:32

可能是最有效的方式做到这一点(如果你真的想这样做)是调用ReadFileScatter()前后插入点之后读取数据块,在FILE_SEGMENT_ELEMENT[3]列表的中间插入新的数据和呼叫WriteFileGather()。是的,这涉及在磁盘上移动字节。但是你将困难的部分留给了操作系统。

我注意到了paxdiablo在处理其他应用程序时的回答,以及Matteo Italia对可安装文件系统的评论。这让我意识到还有另一个不平凡的解决方案。

使用重新分析点,您可以从基本文件加上增量变量创建一个“虚拟”文件。任何不知道此方法的应用程序都会看到连续的字节范围,因为增量通过文件系统过滤器实时应用。对于小型增量(总计< 16 KB),增量信息可以存储在重新分析点本身;可以将更大的增量放置在替代数据流中。当然不平凡。