我正在编写一个库,用于处理二进制文件.具体地说是"日志(log)信息标准(LIS)79子集",它具有具有各种数据类型的条目的各种类型的记录.每个条目可以是单个值、数组或具有更复杂的 struct .文件大小可能从3-5 KB到几GB不等.

Goal:读取和修改文件的任意大小的任何部分.

What has been tried:

  • 首先,实现只需读取整个文件,然后将每个记录和数据类型连同其缓冲区一起写入相应的类实例.对于小文件来说,它非常好,但当文件大小超过1 GB时,它会非常慢,并且需要大量内存.
  • 然后,我试图完全停止使用缓冲区.读取每个组件的文件、存储的偏移量、大小和类型.这种方法对阅读效果很好,但我经过研究后了解到,没有办法在文件的随机位置插入数据.

因此,问题是如何在不过度使用RAM的情况下正确处理这些数据?

推荐答案

我对LIS文件一无所知,因此这将是关于二进制文件的一般内容.

许多二进制文件格式将具有某种类型的索引,以及实际的数据条目本身.因此,读取文件将包括扫描整个索引,直到找到您要查找的内容,然后跳到索引中指定的偏移量.索引可以定义在文件开头的块中,也可以定义为分布在整个文件中的链表.实际的形式可能会复杂得多,但作为一个简化的心理模型可能会很有用.

如果您知道格式,您可以简单地使用BinaryReader来读取值,并相应地在文件中跳转.可能是使用某种状态机来跟踪您正在阅读的内容.

在文件的任意位置插入数据

这是really difficult分才能做得好.您将不得不在浪费空间、移动数据和碎片之间做出 Select .数据库花费了大量的精力试图在每个极端之间找到一个令人满意的中间结果.

但是,如果您使用的是现有格式,则会为您做出 Select .如果该格式不是为廉价的插入而设计的,那么您可能需要移动文件中的绝大多数数据,基本上需要您重写整个内容.如果您幸运的话,这种格式可能允许以较低的成本追加数据.

如果该格式不是为廉价修改而设计的,那么您很可能需要将其转换为修改成本为is%的格式.如果你能把它们都保存在内存中,那很可能会让事情变得简单.

您还可以将索引解析到内存 struct 中,并将任何更新保留在内存中,直到将数据写回磁盘.因此,想象中的格式可能是这样的.这里的关键是您只需要从磁盘读取最少的数据量,并且添加或修改是在内存中完成的.请注意,这仅用于说明目的.

public class Index
{
    private readonly Dictionary<string, IEntry> entries = new();

    public IEnumerable<string> List => entries.Keys;
    public byte[] Read(string key) => entries[key].Read();
    public void UpdateOrAdd(string key, byte[] data) => entries[key] = new MemoryEntry(data);

    public static Index Load(Stream source)
    {
        var br = new BinaryReader(source);
        var numEntries = br.ReadInt32();
        var result = new Index();
        
        for (int i = 0; i < numEntries; i++)
        {
            var key = br.ReadString();
            var length = br.ReadInt32();

            // Note. Mixing index information and data like this will make it 
            // easy to read/append, but slower to load. 
            var offset = (int)br.BaseStream.Position;
            result.entries[key] = new FileEntry(source, offset, length);
            br.BaseStream.Position += length;
        }
        return result;
    }

    public void Save(Stream destination)
    {
        var bw = new BinaryWriter(destination);
        bw.Write(entries.Count);
        var list = entries.ToList();
        foreach (var (key, value) in list)
        {
            bw.Write(key);
            bw.Write(value.Length);
            value.CopyTo(bw.BaseStream);
        }
    }
}

public interface IEntry
{
    public void CopyTo(Stream destination);
    public byte[] Read();
    public int Length { get; }
}

public class MemoryEntry : IEntry
{
    private readonly byte[] data;
    public MemoryEntry(byte[] data) => this.data = data;
    public void CopyTo(Stream destination) => destination.Write(data, 0, data.Length);
    public byte[] Read() => data;
    public int Length => data.Length;
}

public class FileEntry : IEntry
{
    private readonly Stream fileStream;
    private readonly int offset;
    private readonly int length;

    public FileEntry(Stream fileStream, int offset, int length)
    {
        this.fileStream = fileStream;
        this.offset = offset;
        this.length = length;
    }

    public void CopyTo(Stream destination)
    {
        fileStream.Position = offset;
        fileStream.CopyTo(destination, length);
    }

    public byte[] Read()
    {
        fileStream.Position = offset;
        var result = new byte[length];
        fileStream.Position = offset;
        var bytesRead = fileStream.Read(result, 0, length);
        if (bytesRead != length) throw new InvalidOperationException("Invalid binary format");
        return result;
    }

    public int Length => length;
}

Csharp相关问答推荐

使用yaml将Azure函数代码部署到FunctionApp插槽时出现问题(zip未找到)

.NET 6控制台应用程序,RabbitMQ消费不工作时,它的程序文件中的S

在静态模式下实例化配置

JSON空引用异常仅在调试器中忽略try-Catch块,但在其他上下文中捕获得很好

MigraDoc文档

为具有实体框架后端的Reaction项目 Select 正确的Visual Studio模板

如何将DotNet Watch与发布配置和传递给应用程序的参数一起使用?

Selify只更改第一个下拉菜单,然后忽略REST-C#

什么类型的对象存储在大对象堆(LOH)中

当使用Dapper映射DBNull时,我可以抛出异常吗?

异步任务调用程序集

Blazor Server.NET 8中的Blazore.FluentValidation问题

CRL已过期,但ChainStatus告诉我RevocationStatus未知

MSI无法将快捷方式添加到启动文件夹

从Base64转换为不同的字符串返回相同的结果

.NET Google Workspace API获取错误CS0266

如何正确处置所有动态控件?

C#System.Commandline:如何向命令添加参数以便向其传递值?

如何消除Visual Studio错误,因为它不识别集合表达式的新C#12语法?

如何正确地在VB中初始化类?