我有以下代码片段,用于读取二进制文件并验证它:
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# 中执行此操作?
最佳答案
定义一个struct
(可能是 readonly struct
),具有与您的 C++ 代码完全相同的显式布局( [StructLayout(LayoutKind.Explicit)]
),然后是以下之一:
- 将文件作为内存映射文件打开,获取指向数据的指针;使用
unsafe
原始指针上的代码,或使用Unsafe.AsRef<YourStruct>
关于数据,以及Unsafe.Add<>
迭代 - 将文件作为内存映射文件打开,获取指向数据的指针; create a custom memory (您的
T
)的指针,并迭代跨度 - 将文件打开为
byte[]
;创建Span<byte>
超过byte[]
,然后使用MemoryMarshal.Cast<,>
创建Span<YourType>
,并迭代它 - 将文件打开为
byte[]
;使用fixed
固定byte*
并得到一个指针;使用unsafe
遍历指针的代码 - 涉及“管道”的东西 - a
Pipe
那是缓冲区,也许使用StreamConnection
在FileStream
上用于填充管道,以及从管道中出列的工作循环;复杂性:缓冲区可能不连续,并且可能在不方便的地方分割;只要第一个跨度不是至少 8 个字节,就需要可解决的但微妙的代码
(或这些概念的某种组合)
其中任何一个都应该像您的 C++ 版本一样工作。第四个很简单,但对于非常大的数据,您可能更喜欢内存映射文件
关于c# - 从文件中高效读取结构化二进制数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59825404/