如何将字节数组解析为对象?

时间:2011-06-08 06:36:19

标签: c# .net

我正在读取几年前预定义的二进制数据(通过网络流或文件)。我目前通过将其读入字节数组来读取此数据,然后将数组转换为System.BitConverter所需的字段。毋庸置疑,这是耗费时间和容易出错的。

我希望我可以使用ISerializable,但我不知道如何为预定义的结构实现这一点。

我希望能够指出如何改进我目前的策略......

4 个答案:

答案 0 :(得分:5)

Rozon,我遇到了同样的问题,在这里我发现这个问题之前,我正要放弃解决方案,并阅读了Joe对此的回应。 Joe提到Marshal对象在常规的 MEMORY 和一个托管对象之间移动数据......这就是我所做的,以及对我来说像魔术的作用......如果你还没有想到这一点,这是对我所做的事情的全面细分,以及我是如何让它发挥作用的......(谢谢乔!)......

首先,您必须能够创建结构的托管实例。这不应该太难,它只是意味着你的结构有很多额外的属性。

例如,这是一个包含有关服务器的一些信息的数据结构。当然,字符串大小不准确,所以请确保您的字符串大小为!

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=1)]
struct TServerInformation {
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=255)]
    public string ComputerName;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=255)]
    public string HostName;

    [MarshalAs(UnmanagedType.U4)]
    public Int32 IPAddress;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]
    public string AdminName;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)]
    public string WindowsDir;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=255)]
    public string Domain;
};

另外,如果您正在使用正确的Charset,如果它被编译为使用Unicode,则需要确保Charset能够反映出来。

现在,这里很酷......

要获取结构的SIZE,(以及需要保存缓冲区的大小),只需使用:

byte[] buff = new byte[Marshal.SizeOf(pStruct)];

而且,有了这个,你现在可以做任何你做的事情来获取传递给你的这些数据(我假设你已经找到了所有这些......)然后魔术就可以开始了。我们所做的是分配一块非托管内存,然后将缓冲区复制到内存块中,然后将其复制回我们的结构中,并释放非托管内存块。这可以很容易地放入一个可以在任何byte []上运行的扩展函数,使你能够调用类似的东西:

pStruct = (TServerInformation)buff.FromMemory();

以下代码对我有用。哦,不要忘记在生产代码中放置try / catch / finally块...

// Create our Managed Object...
TServerInformation pStruct = new TServerInformation();
// Allocate our block of unmanaged memory...
IntPtr pMem = Marshal.AllocHGlobal(Marshal.SizeOf(pStruct));
// Copy our buff buffer into that new block of memory...
Marshal.Copy(buff, 0, pMem, buff.Length);
// Type-cast that block of memory with the results of the Marshal object's help...
pStruct = (TServerInformation)Marshal.PtrToStructure(pMem, pStruct.GetType());
// Free that block of memory, that is no longer needed now that the data exists in managed form.
Marshal.FreeHGlobal(pMem);

你有它!这样可以避免使用序列化时包含的所有额外标题信息,而且,这是我发现的唯一可以获得准确" sizeof()"来自托管结构的数据。我确定可能有其他方法可以做到这一点,但到目前为止,这是我发现的最好的,经过大约5天的搜索,黑客攻击,搜索,调试,黑客攻击和搜索更多...: )

如果这对你有用,请告诉我......我很想知道它是否能解决你的问题。

答案 1 :(得分:4)

一种方法是在字节数组周围创建MemoryStream,然后使用BinaryReader。这使您可以非常轻松地解析原始值等。

但是,它取决于数据的endianness是否合适。我在MiscUtil中有一个EndianBinaryReader课程,如果内置的课程不合适,可以帮助您。

答案 2 :(得分:2)

Marshal互操作类和方法(即PtrToStruct和StructToPtr)也可以在这里提供帮助。请参阅:http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx

答案 3 :(得分:0)

希望这有帮助:

  //set your byte data instead of null
        byte[] data = null;

        MemoryStream stream = new MemoryStream();
        stream.Write(data,0,data.Length);

        BinaryFormatter formatter = new BinaryFormatter();

        Type s1 = (Type)formatter.Deserialize(stream);