首页 > 解决方案 > 从文件中高效读取结构化二进制数据

问题描述

我有以下代码片段可以读取二进制文件并对其进行验证:

 FileStream f = File.OpenRead("File.bin");
 MemoryStream memStream = new MemoryStream();
 memStream.SetLength(f.Length);
 f.Read(memStream.GetBuffer(), 0, (int)f.Length);
 f.Seek(0, SeekOrigin.Begin);
 var r = new BinaryReader(f);
 Single prevVal=0;
 do
 {
    r.ReadUInt32();
    var val = r.ReadSingle();
    if (prevVal!=0) {
       var diff = Math.Abs(val - prevVal) / prevVal;
       if (diff > 0.25)
          Console.WriteLine("Bad!");
    }
    prevVal = val;
 }
 while (f.Position < f.Length);

不幸的是,它工作得非常缓慢,我正在寻求改进。在 C++ 中,我只需将文件读入字节数组,然后将该数组重铸为结构数组:

struct S{
   int a;
   float b;
}

我将如何在 C# 中执行此操作?

标签: c#optimizationbinaryfiles

解决方案


使用与您的 C++ 代码完全相同的显式布局 ( )定义 a struct(可能是 a ),然后是:readonly struct[StructLayout(LayoutKind.Explicit)]

  1. 将文件作为内存映射文件打开,获取指向数据的指针;unsafe在原始指针上使用代码,或Unsafe.AsRef<YourStruct>在数据上使用,并Unsafe.Add<>进行迭代
  2. 将文件作为内存映射文件打开,获取指向数据的指针;在(您的)指针上创建自定义内存T,并在跨度上进行迭代
  3. 将文件打开为byte[]; Span<byte>在 上创建 a byte[],然后用于MemoryMarshal.Cast<,>创建 a Span<YourType>,并对其进行迭代
  4. 将文件打开为byte[]; 用于fixed固定byte*并获取指针;使用unsafe代码走指针
  5. 一些涉及“管道”的东西 - aPipe是缓冲区,可能StreamConnection用于FileStream填充管道,以及从管道中出列的工作循环;复杂性:缓冲区可能不连续,并且可能在不方便的地方拆分;可解,但只要第一个跨度不是至少 8 个字节,就需要微妙的代码

(或这些概念的某种组合)

其中任何一个都应该像您的 C++ 版本一样工作。第四种很简单,但是对于非常大的数据,您可能希望更喜欢内存映射文件


推荐阅读