首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在MessagePack中保存和附加到序列化的C#二进制文件?

如何在MessagePack中保存和附加到序列化的C#二进制文件?
EN

Stack Overflow用户
提问于 2019-11-09 14:29:01
回答 1查看 2.9K关注 0票数 5

我试图使用MessagePack保存多个结构列表,因为我看到它的性能优于BinaryFormatter序列化。

我要做的是接收实时时间序列数据,并定期将其保存(附加)到磁盘,例如,如果列表中的元素数为100。我的问题是:

1)在这种情况下,序列化结构列表并异步保存到磁盘是否更好?

2)如何使用MessagePack简单地将其保存到磁盘中?

代码语言:javascript
运行
复制
public struct struct_realTime
{
    public int indexNum { get; set; }
    public string currentTime { get; set; }
    public string currentType { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        List<struct_realTime> list_temp = new List<struct_realTime>(100000);

        for (int num=0; num < 100000; num++)
        {
            list_temp.Add(new struct_realTime
            {
                indexNum = 1,
                currentTime = "time",
                currentType = "type",
            });
        }

        string filename = "file.bin";

        using (var fileStream = new FileStream(filename, FileMode.Append, FileAccess.Write))
        {
            byte[] bytes = MessagePackSerializer.Serialize(list_temp);
            Console.WriteLine(MessagePackSerializer.ToJson(bytes));
        }
    }
}

当我运行这段代码时,它会创建file.bin并打印出100000个结构,但是文件是0字节。

当我使用BinaryFormatter时,我会这样做:

代码语言:javascript
运行
复制
using (var fileStream = new FileStream("file.bin", FileMode.Append))
{
    BinaryFormatter formatter = new BinaryFormatter();
    formatter.Serialize(fileStream, list_temp);
}

我怎样才能解决这个问题?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-11-11 21:46:24

您要做的是将使用List<struct_realTime>序列化的对象(此处为MessagePackSerializer )附加到包含已序列化的类似对象序列的文件中,就像使用BinaryFormatter原状网Json.NET一样。稍后,您可能希望能够将整个序列反序列化为相同类型的对象的列表或数组。

您的代码有三个问题,两个简单,一个基本。

简单的问题如下:

  • 您实际上并不会写信给fileStream。相反,请执行以下操作: //依次使用(var fileStream =新FileStream(filename,FileMode.OpenOrCreate,FileAccess.ReadWrite)) { MessagePackSerializer.Serialize(fileStream,list_temp);}
  • 您还没有用struct_realTime标记属性。这可以实施如下: MessagePackObject公共结构struct_realTime { Key(0) public int indexNum { get;set;} Key(1)公共字符串currentTime { get;set;} Key(2)公钥currentType { get;set;}

这样做之后,您现在可以重复地将list_temp序列化为一个文件.但你以后就看不懂了!这是因为MessagePackSerializer在反序列化根对象时似乎读取了整个文件,跳过了附加在文件中的任何其他数据。因此,像下面这样的代码将失败,因为只有一个对象从文件中读取:

代码语言:javascript
运行
复制
List<List<struct_realTime>> allItemsInFile = new List<List<struct_realTime>>();
using (var fileStream = File.OpenRead(filename))
{
    while (fileStream.Position < fileStream.Length)
    {
        allItemsInFile.Add(MessagePackSerializer.Deserialize<List<struct_realTime>>(fileStream));                   
    }
}
Assert.IsTrue(allItemsInFile.Count == expectedNumberOfRootItemsInFile);

演示小提琴#1 这里

下面这样的代码将失败,因为流中的(第一个)根对象不是对象数组的数组,而是一个数组:

代码语言:javascript
运行
复制
List<List<struct_realTime>> allItemsInFile;
using (var fileStream = File.OpenRead(filename))
{
    allItemsInFile = MessagePackSerializer.Deserialize<List<List<struct_realTime>>>(fileStream);
}
Assert.IsTrue(allItemsInFile.Count == expectedNumberOfRootItemsInFile);

演示小提琴#2 这里

由于MessagePackSerializer似乎缺乏从流中反序列化多个根对象的能力,您的选项是什么?首先,您可以反序列化一个List<List<struct_realTime>>,并附加到它,然后将整个程序序列化回文件中。大概你不想这么做是因为性能方面的原因。

其次,直接使用MessagePack规范,您可以手动查找文件的开头,解析和重写一个适当的格式标头,然后查找到文件的末尾,并使用MessagePackSerializer来序列化和追加新项。以下扩展方法执行此任务:

代码语言:javascript
运行
复制
public static class MessagePackExtensions
{
    const byte Array32 = 0xdd;
    const int Array32HeaderLength = 5;

    public static void AppendToFile<T>(Stream stream, T item)
    {
        if (stream == null)
            throw new ArgumentNullException(nameof(stream));
        if (!stream.CanSeek)
            throw new ArgumentException("!stream.CanSeek");

        stream.Position = 0;
        var buffer = new byte[Array32HeaderLength];
        var read = stream.Read(buffer, 0, Array32HeaderLength);
        stream.Position = 0;
        if (read == 0)
        {
            FormatArray32Header(buffer, 1);
            stream.Write(buffer, 0, Array32HeaderLength);
        }
        else
        {
            var count = ParseArray32Header(buffer, read);
            FormatArray32Header(buffer, count + 1);
            stream.Write(buffer, 0, Array32HeaderLength);
        }

        stream.Position = stream.Length;
        MessagePackSerializer.Serialize(stream, item);
    }

    static void FormatArray32Header(byte [] buffer, uint value)
    {
        buffer[0] = Array32;
        buffer[1] = unchecked((byte)(value >> 24));
        buffer[2] = unchecked((byte)(value >> 16));
        buffer[3] = unchecked((byte)(value >> 8));
        buffer[4] = unchecked((byte)value);
    }

    static uint ParseArray32Header(byte [] buffer, int readCount)
    {
        if (readCount < 5 || buffer[0] != Array32)
            throw new ArgumentException("Stream was not positioned on an Array32 header.");
        int i = 1;
        //https://stackoverflow.com/questions/8241060/how-to-get-little-endian-data-from-big-endian-in-c-sharp-using-bitconverter-toin
        //https://stackoverflow.com/a/8241127 by https://stackoverflow.com/users/23354/marc-gravell
        var value = unchecked((uint)((buffer[i++] << 24) | (buffer[i++] << 16) | (buffer[i++] << 8) | buffer[i++]));
        return value;
    }
}

它可以用于追加list_temp,如下所示:

代码语言:javascript
运行
复制
// Append each entry sequentially
using (var fileStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
    MessagePackExtensions.AppendToFile(fileStream, list_temp);
}

然后,要反序列化整个文件,请执行以下操作:

代码语言:javascript
运行
复制
List<List<struct_realTime>> allItemsInFile;
using (var fileStream = File.OpenRead(filename))
{
    allItemsInFile = MessagePackSerializer.Deserialize<List<List<struct_realTime>>>(fileStream);
}

备注:

  • MessagePack协议有3种不同的数组格式:
代码语言:javascript
运行
复制
- `fixarray` stores an array whose length is up to 15 elements.
- `array 16` stores an array whose length is up to (2^16)-1 elements.
- `array 32` stores an array whose length is up to (2^32)-1 elements.

扩展方法要求根数组为array 32,以避免在新的大小超过fixarrayarray 16的容量时重新格式化整个数组的需要。然而,MessagePackSerializer总是以最紧凑的格式写入,因此附加到以前由MessagePackSerializer序列化的集合并不一定能工作。

演示小提琴#3 这里

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58780225

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档