C# - 删除大型二进制文件中的字节块
C# - remove blocks of bytes in large binary files
我想在 c# 中使用一种快速的方法从大小在 500MB 到 1GB 之间的二进制文件中删除不同位置的字节块,需要删除的字节的开始和长度在保存的数组中
int[] rdiDataOffset= {511,15423,21047};
int[] rdiDataSize={102400,7168,512};
编辑:
这是我的一段代码,除非我将缓冲区大小设置为 1,否则它将无法正常工作:
while(true){
if (rdiDataOffset.Contains((int)fsr.Position))
{
int idxval = Array.IndexOf(rdiDataOffset, (int)fsr.Position, 0, rdiDataOffset.Length);
int oldRFSRPosition = (int)fsr.Position;
size = rdiDataSize[idxval];
fsr.Seek(size, SeekOrigin.Current);
}
int bufferSize = size == 0 ? 2048 : size;
if ((size>0) && (bufferSize > (size))) bufferSize = (size);
if (bufferSize > (fsr.Length - fsr.Position)) bufferSize = (int)(fsr.Length - fsr.Position);
byte[] buffer = new byte[bufferSize];
int nofbytes = fsr.Read(buffer, 0, buffer.Length);
fsr.Flush();
if (nofbytes < 1)
{
break;
}
}
没有任何通用文件系统提供从现有文件中间删除块的有效方法(仅从末尾截断)。您必须在删除后将所有数据复制回适当的新位置。
使用临时文件执行此操作的简单算法(也可以就地完成,但万一出现问题,风险会更高)。
创建一个新文件并调用SetLength to set the stream size (if this is too slow you can Interop to SetFileValidData)。这可确保您在进行复制时为临时文件留出空间。
按升序对您的删除列表进行排序。
从当前位置(从0开始)读取到第一个移除点。应在不授予写入共享权限的情况下打开源文件(您不希望有人在您编辑它时乱搞它)。
将该内容写入新文件(您可能需要分块执行此操作)。
跳过未复制的数据
从#3 重复直到完成
您现在有两个文件 - 旧的和新的...根据需要替换。如果这确实是关键数据,您可能希望采用事务处理方法(您实施的方法或使用类似 NTFS 事务的方法)。
考虑一个新的设计。如果这是您需要经常做的事情,那么在文件(或文件附近)中包含一个包含非活动块列表的索引可能更有意义 - 然后在必要时您可以通过实际删除块来压缩文件.. . 或者这就是那个过程。
如果您使用的是 NTFS 文件系统(大多数 Windows 部署都是)并且您不介意使用 p/invoke 方法,那么有一种更快的删除块的方法从一个文件。您可以使文件稀疏。使用稀疏文件,您可以通过一次调用消除大块文件。
执行此操作时,不会重写文件。相反,NTFS 更新有关清零数据范围的元数据。稀疏文件的优点在于您的文件的使用者不必知道文件的稀疏性。也就是说,当您通过稀疏文件从 FileStream 读取数据时,清零范围将被透明地跳过。
NTFS 将此类文件用于自己的簿记。例如,USN 日志是一个非常大的稀疏内存映射文件。
使文件稀疏和零输出部分的方法是使用 DeviceIOControl windows API。它是神秘的并且需要 p/invoke 但如果你走这条路,你肯定会隐藏漂亮函数调用背后的问题。
有一些问题需要注意。例如,如果将文件移动到非 ntfs 卷然后再移回,文件的稀疏性可能会消失 - 因此您应该进行防御性编程。
此外,稀疏文件可能看起来比实际大 - 使涉及磁盘配置的任务复杂化。已完全清零的 5g 稀疏文件仍将 5g 计入用户的磁盘配额。
如果一个稀疏文件积累了很多漏洞,您可能希望偶尔在维护中重写该文件 window。我还没有看到任何真正的性能问题发生,但我至少可以想象瑞士俗气的稀疏文件的元数据可能会导致一些性能下降。
这里 link 给一些 doc 如果你喜欢这个想法。
我想在 c# 中使用一种快速的方法从大小在 500MB 到 1GB 之间的二进制文件中删除不同位置的字节块,需要删除的字节的开始和长度在保存的数组中
int[] rdiDataOffset= {511,15423,21047};
int[] rdiDataSize={102400,7168,512};
编辑: 这是我的一段代码,除非我将缓冲区大小设置为 1,否则它将无法正常工作:
while(true){
if (rdiDataOffset.Contains((int)fsr.Position))
{
int idxval = Array.IndexOf(rdiDataOffset, (int)fsr.Position, 0, rdiDataOffset.Length);
int oldRFSRPosition = (int)fsr.Position;
size = rdiDataSize[idxval];
fsr.Seek(size, SeekOrigin.Current);
}
int bufferSize = size == 0 ? 2048 : size;
if ((size>0) && (bufferSize > (size))) bufferSize = (size);
if (bufferSize > (fsr.Length - fsr.Position)) bufferSize = (int)(fsr.Length - fsr.Position);
byte[] buffer = new byte[bufferSize];
int nofbytes = fsr.Read(buffer, 0, buffer.Length);
fsr.Flush();
if (nofbytes < 1)
{
break;
}
}
没有任何通用文件系统提供从现有文件中间删除块的有效方法(仅从末尾截断)。您必须在删除后将所有数据复制回适当的新位置。
使用临时文件执行此操作的简单算法(也可以就地完成,但万一出现问题,风险会更高)。
创建一个新文件并调用SetLength to set the stream size (if this is too slow you can Interop to SetFileValidData)。这可确保您在进行复制时为临时文件留出空间。
按升序对您的删除列表进行排序。
从当前位置(从0开始)读取到第一个移除点。应在不授予写入共享权限的情况下打开源文件(您不希望有人在您编辑它时乱搞它)。
将该内容写入新文件(您可能需要分块执行此操作)。
跳过未复制的数据
从#3 重复直到完成
您现在有两个文件 - 旧的和新的...根据需要替换。如果这确实是关键数据,您可能希望采用事务处理方法(您实施的方法或使用类似 NTFS 事务的方法)。
考虑一个新的设计。如果这是您需要经常做的事情,那么在文件(或文件附近)中包含一个包含非活动块列表的索引可能更有意义 - 然后在必要时您可以通过实际删除块来压缩文件.. . 或者这就是那个过程。
如果您使用的是 NTFS 文件系统(大多数 Windows 部署都是)并且您不介意使用 p/invoke 方法,那么有一种更快的删除块的方法从一个文件。您可以使文件稀疏。使用稀疏文件,您可以通过一次调用消除大块文件。
执行此操作时,不会重写文件。相反,NTFS 更新有关清零数据范围的元数据。稀疏文件的优点在于您的文件的使用者不必知道文件的稀疏性。也就是说,当您通过稀疏文件从 FileStream 读取数据时,清零范围将被透明地跳过。
NTFS 将此类文件用于自己的簿记。例如,USN 日志是一个非常大的稀疏内存映射文件。
使文件稀疏和零输出部分的方法是使用 DeviceIOControl windows API。它是神秘的并且需要 p/invoke 但如果你走这条路,你肯定会隐藏漂亮函数调用背后的问题。
有一些问题需要注意。例如,如果将文件移动到非 ntfs 卷然后再移回,文件的稀疏性可能会消失 - 因此您应该进行防御性编程。
此外,稀疏文件可能看起来比实际大 - 使涉及磁盘配置的任务复杂化。已完全清零的 5g 稀疏文件仍将 5g 计入用户的磁盘配额。
如果一个稀疏文件积累了很多漏洞,您可能希望偶尔在维护中重写该文件 window。我还没有看到任何真正的性能问题发生,但我至少可以想象瑞士俗气的稀疏文件的元数据可能会导致一些性能下降。
这里 link 给一些 doc 如果你喜欢这个想法。