我希望从二进制文件中读取数据结构。 在C++中,我会这样做:
stream.read((char*)&someStruct, sizeof(someStruct));
在C#中有类似的方法吗?BinaryReader只适用于内置类型。在.NET 4中,有一个MemoryMappedViewAccessor。它提供了像Read<T>
这样的方法,看起来是我想要的,但我必须手动跟踪我想读取文件中的哪个位置。
有更好的方法吗?
我希望从二进制文件中读取数据结构。 在C++中,我会这样做:
stream.read((char*)&someStruct, sizeof(someStruct));
在C#中有类似的方法吗?BinaryReader只适用于内置类型。在.NET 4中,有一个MemoryMappedViewAccessor。它提供了像Read<T>
这样的方法,看起来是我想要的,但我必须手动跟踪我想读取文件中的哪个位置。
有更好的方法吗?
public static class StreamExtensions
{
public static T ReadStruct<T>(this Stream stream) where T : struct
{
var sz = Marshal.SizeOf(typeof(T));
var buffer = new byte[sz];
stream.Read(buffer, 0, sz);
var pinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var structure = (T) Marshal.PtrToStructure(
pinnedBuffer.AddrOfPinnedObject(), typeof(T));
pinnedBuffer.Free();
return structure;
}
}
SomeStruct s = stream.ReadStruct<SomeStruct>();
Marshal.PtrToStructure()
在枚举类型上抛出异常(可能是因为无法在枚举上使用[StructLayout]
?)。对于枚举,您可以使用typeof(T).GetEnumUnderlyingType()
,它可以正常工作。 - Carl Walsh
public static T? ReadStructure<T>(this Stream stream) where T : struct
{
if (stream == null)
return null;
int size = Marshal.SizeOf(typeof(T));
byte[] bytes = new byte[size];
if (stream.Read(bytes, 0, size) != size) // can't build this structure!
return null;
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
try
{
return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
}
finally
{
handle.Free();
}
}
为了详细说明Guffa和jesperll的答案,这里提供一个示例,使用基本相同的ReadStruct
方法(只是不作为扩展方法)来读取ASF(WMV / WMA)文件的文件头。
MemoryStream ms = new MemoryStream(headerData);
AsfFileHeader asfFileHeader = ReadStruct<AsfFileHeader>(ms);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
internal struct AsfFileHeader
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] object_id;
public UInt64 object_size;
public UInt32 header_object_count;
public byte r1;
public byte r2;
}
在C#中没有类似的方法。此外,由于其不可移植性,这种序列化方式已经被弃用了。建议使用http://www.codeproject.com/KB/cs/objserial.aspx代替。