【问题标题】:Deserializing a byte array反序列化字节数组
【发布时间】:2011-09-29 00:55:25
【问题描述】:

如果我想从二进制文件中填充结构,我会使用这样的东西:

using (BinaryReader br = new BinaryReader(File.Open(filename, FileMode.Open)))
{
    myStruct.ID = br.ReadSingle();
    myStruct.name = br.ReadBytes(20);
}

但是,我必须在反序列化之前将整个文件读入一个字节数组,因为我想做一些预处理。是否有任何托管方法可以从字节数组中填充我的结构,最好类似于上述方法?

【问题讨论】:

  • 您应该考虑使您的类型可序列化。如果这是你感兴趣的东西,我会提供一个样本。有关二进制序列化,请参阅“BinaryFormatter”。
  • 根据我的经验,BinaryFormatter 很少是序列化数据的正确选择。
  • 这在很大程度上取决于具体情况。我通常需要一个定义明确的文件格式,所以我使用 Linq-to-Json 或 Linq-to-Xml 之类的东西在我的内存表示和文件格式之间进行转换。有时 protobuf 很好,因为它非常紧凑。在极少数情况下,如果您不需要版本控制并且可以忍受其具有深度侵入性的性质,BinaryFormatter 可能是正确的选择。游戏中的存档游戏是少数符合 BinaryFormatterIMO 的案例之一。
  • BinaryFormatter 的另一个大问题是您必须绝对信任该文件。创建该文件的人最有可能在您的程序上下文中执行代码。
  • Linq-To-Json/Xml 在技术上不是序列化。表示之间的转换是手动完成的,这显然需要更多的工作。但我认为对于稳定的文件格式,内存表示和存储格式的清晰分离通常值得额外的工作。

标签: c# binary managed deserialization


【解决方案1】:

看看BitConverter 类。这可能会满足您的需求。

【讨论】:

  • 感谢您的回答,该课程确实满足了我的需求。唯一的小不便是我需要手动跟踪位置。
【解决方案2】:

这是一个示例,用于获取一些数据(实际上是 System.Data.DataSet)并序列化为字节数组,同时使用 DeflateStream 进行压缩。

try
{
    var formatter = new BinaryFormatter();
    byte[] content;
    using (var ms = new MemoryStream())
    {
         using (var ds = new DeflateStream(ms, CompressionMode.Compress, true))
         {
             formatter.Serialize(ds, set);
         }
         ms.Position = 0;
         content = ms.GetBuffer();
         contentAsString = BytesToString(content);
     }
}
catch (Exception ex) { /* handle exception omitted */ }

下面是反序列化的代码:

        var set = new DataSet();
        try
        {
            var content = StringToBytes(s);
            var formatter = new BinaryFormatter();
            using (var ms = new MemoryStream(content))
            {
                using (var ds = new DeflateStream(ms, CompressionMode.Decompress, true))
                {
                    set = (DataSet)formatter.Deserialize(ds);                        
                }
            }
        }
        catch (Exception ex)
        {
            // removed error handling logic!
        }

希望这会有所帮助。正如 Nate 所暗示的,我们在这里使用 MemoryStream。

【讨论】:

  • 非常感谢,这对大型结构应该更有帮助。只是一个问题 - 更改结构对齐会影响反序列化结果吗?
  • 我认为结构对齐会影响序列化和反序列化,但我不确定。
  • @Angel 作为记录,我强烈建议 反对 BinaryFormatter 用于 any 类型的存储。它有几个令人讨厌的怪癖,老实说,我已经记不清有多少次我不得不帮助那些为丢失数据等而苦苦挣扎的人。存在其他序列化程序(IMO,基于序列化方面的大量工作)更健壮,只是一个方便,并且在 CPU 和带宽方面都更加高效。
  • 嘿,马克 - 我在我现在正在处理的代码中使用这个示例,我使用的是 XmlSerializer,但切换到了 BinaryFormatter。我应该回去吗?还有哪些其他选择?
  • @Glenn 这取决于你想用序列化器做什么。
【解决方案3】:

对于不可序列化且仅包含基本类型的非常简单的结构,这是可行的。我用它来解析具有已知格式的文件。为了清楚起见,删除了错误检查。

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;

namespace FontUtil
{
    public static class Reader
    {
        public static T Read<T>(BinaryReader reader, bool fileIsLittleEndian = false)
        {
            Type type = typeof(T);
            int size = Marshal.SizeOf(type);
            byte[] buffer = new byte[size];
            reader.Read(buffer, 0, size);
            if (BitConverter.IsLittleEndian != fileIsLittleEndian)
            {
                FieldInfo[] fields = type.GetFields();
                foreach (FieldInfo field in fields)
                {
                    int offset = (int)Marshal.OffsetOf(type, field.Name);
                    int fieldSize = Marshal.SizeOf(field.FieldType);
                    for (int b = offset, t = fieldSize + b - 1; b < t; ++b, --t)
                    {
                        byte temp = buffer[t];
                        buffer[t] = buffer[b];
                        buffer[b] = temp;
                    }
                }
            }
            GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            T obj = (T)Marshal.PtrToStructure(h.AddrOfPinnedObject(), type);
            h.Free();
            return obj;
        }
    }
}

需要像这样声明结构(并且不能包含数组,我认为,还没有尝试过 - 字节序交换可能会混淆)。

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct NameRecord
{
    public UInt16 uPlatformID;
    public UInt16 uEncodingID;
    public UInt16 uLanguageID;
    public UInt16 uNameID;
    public UInt16 uStringLength;
    public UInt16 uStringOffset; //from start of storage area
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 2018-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多