gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 02:42:20 26 4
gpt4 key购买 nike

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

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

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

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

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 ,我这样做:
using (var fileStream = new FileStream("file.bin", FileMode.Append))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fileStream, list_temp);
}

我该如何解决这个问题?

最佳答案

您要做的是附加使用 List<struct_realTime> 序列化的对象(此处为 MessagePackSerializer )到一个包含类似对象的已经序列化序列的文件,与 BinaryFormatter 可能的方式相同, protobuf-netJson.NET .稍后,您可能希望能够将整个序列反序列化为相同类型的对象列表或数组。

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

简单的问题如下:

  • 您实际上并没有写信给 fileStream .相反,请执行以下操作:
    // Append each list_temp sequentially
    using (var fileStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite))
    {
    MessagePackSerializer.Serialize(fileStream, list_temp);
    }
  • 您还没有标记struct_realTime [MessagePackObject] attributes .这可以实现,例如如下:
    [MessagePackObject]
    public struct struct_realTime
    {
    [Key(0)]
    public int indexNum { get; set; }
    [Key(1)]
    public string currentTime { get; set; }
    [Key(2)]
    public string currentType { get; set; }
    }

  • 完成后,您现在可以重复序列化 list_temp到一个文件...但你以后将无法读取它们!那是因为 MessagePackSerializer反序列化根对象时似乎读取了整个文件,跳过了文件中附加的任何其他数据。因此,如下代码将失败,因为从文件中只读取了一个对象:
    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);

    演示 fiddle #1 here .

    像下面这样的代码将失败,因为流中的(第一个)根对象不是对象数组的数组,而只是一个数组:
    List<List<struct_realTime>> allItemsInFile;
    using (var fileStream = File.OpenRead(filename))
    {
    allItemsInFile = MessagePackSerializer.Deserialize<List<List<struct_realTime>>>(fileStream);
    }
    Assert.IsTrue(allItemsInFile.Count == expectedNumberOfRootItemsInFile);

    演示 fiddle #2 here .

    MessagePackSerializer似乎缺乏从流中反序列化多个根对象的能力,您有什么选择?首先,您可以反序列化 List<List<struct_realTime>> ,追加到它,然后将整个东西序列化回文件。大概出于性能原因,您不想这样做。

    其次,使用 MessagePack specification直接,你可以手动寻找文件的开头来解析和重写一个合适的 array 32 format header ,然后查找文件末尾并使用 MessagePackSerializer序列化和附加新项目。以下扩展方法可以完成这项工作:
    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如下:
    // Append each entry sequentially
    using (var fileStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite))
    {
    MessagePackExtensions.AppendToFile(fileStream, list_temp);
    }

    然后,要反序列化整个文件,请执行以下操作:
    List<List<struct_realTime>> allItemsInFile;
    using (var fileStream = File.OpenRead(filename))
    {
    allItemsInFile = MessagePackSerializer.Deserialize<List<List<struct_realTime>>>(fileStream);
    }

    笔记:
  • MessagePack 协议(protocol)有 3 种不同的数组格式:
  • fixarray存储一个长度最多为 15 个元素的数组。
  • array 16存储一个长度最多为 (2^16)-1 个元素的数组。
  • array 32存储一个长度最多为 (2^32)-1 个元素的数组。

  • 扩展方法要求根数组为 array 32当新大小大于 fixarray 的容量时,无需重新格式化整个阵列或 array 16 . MessagePackSerializer但是,将始终写入最紧凑的格式,因此附加到先前由 MessagePackSerializer 序列化的集合中不保证工作。
  • 如果你想使用一个快速的二进制序列化器,它在文件的开头不需要数组计数或大小,从而支持开箱即用的追加操作,请考虑 .详情见I have a Single File And need to serialize multiple objects randomly. How can I in c#?How to append object to a file while serializing using c# protobuf-net? .

    有关如何使用此序列化程序的一般概述,请参阅 https://github.com/protobuf-net/protobuf-net#protobuf-netProtobuf-net: the unofficial manual .您需要使用类似于 MessagePackSerializer 的属性来标记您的类型。 .

  • 演示 fiddle #3 here .

    关于c# - 如何在 C# 中保存并附加到序列化的 MessagePack 二进制文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58780225/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com