我有以下代码片段,该片段读取二进制文件并对其进行验证:
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
)定义与您的C ++代码完全相同的readonly struct
(可能是[StructLayout(LayoutKind.Explicit)]
),然后是以下之一:
unsafe
代码,或在数据上使用Unsafe.AsRef<YourStruct>
,然后使用Unsafe.Add<>
进行迭代T
,并在范围上进行迭代byte[]
格式打开文件;在Span<byte>
上创建一个byte[]
,然后使用MemoryMarshal.Cast<,>
创建一个Span<YourType>
,并在其上进行迭代byte[]
格式打开文件;使用fixed
固定byte*
并获得一个指针;使用unsafe
代码遍历指针(或这些概念的某种组合)
其中任何一个都应该像您的C ++版本一样工作。第四种很简单,但是对于非常大的数据,您可能希望使用内存映射文件
这是我们使用的(与C#的较早版本兼容:]
public static T[] FastRead<T>(FileStream fs, int count) where T: struct
{
int sizeOfT = Marshal.SizeOf(typeof(T));
long bytesRemaining = fs.Length - fs.Position;
long wantedBytes = count * sizeOfT;
long bytesAvailable = Math.Min(bytesRemaining, wantedBytes);
long availableValues = bytesAvailable / sizeOfT;
long bytesToRead = (availableValues * sizeOfT);
if ((bytesRemaining < wantedBytes) && ((bytesRemaining - bytesToRead) > 0))
{
Debug.WriteLine("Requested data exceeds available data and partial data remains in the file.", "Dmr.Common.IO.Arrays.FastRead(fs,count)");
}
T[] result = new T[availableValues];
GCHandle gcHandle = GCHandle.Alloc(result, GCHandleType.Pinned);
try
{
uint bytesRead;
if (!ReadFile(fs.SafeFileHandle, gcHandle.AddrOfPinnedObject(), (uint)bytesToRead, out bytesRead, IntPtr.Zero))
{
throw new IOException("Unable to read file.", new Win32Exception(Marshal.GetLastWin32Error()));
}
Debug.Assert(bytesRead == bytesToRead);
}
finally
{
gcHandle.Free();
}
GC.KeepAlive(fs);
return result;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1415:DeclarePInvokesCorrectly")]
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ReadFile
(
SafeFileHandle hFile,
IntPtr lpBuffer,
uint nNumberOfBytesToRead,
out uint lpNumberOfBytesRead,
IntPtr lpOverlapped
);
对于最新版本的C#,您可以使用Marc在其他答案中提到的Span
!