我试图使用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);
}
我怎样才能解决这个问题?
发布于 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
在反序列化根对象时似乎读取了整个文件,跳过了附加在文件中的任何其他数据。因此,像下面这样的代码将失败,因为只有一个对象从文件中读取:
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 这里。
下面这样的代码将失败,因为流中的(第一个)根对象不是对象数组的数组,而是一个数组:
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
来序列化和追加新项。以下扩展方法执行此任务:
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);
}
备注:
- `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
,以避免在新的大小超过fixarray
或array 16
的容量时重新格式化整个数组的需要。然而,MessagePackSerializer
总是以最紧凑的格式写入,因此附加到以前由MessagePackSerializer
序列化的集合并不一定能工作。
MessagePackSerializer
的属性标记您的类型。演示小提琴#3 这里。
https://stackoverflow.com/questions/58780225
复制相似问题