我已经使用DateTime.Now
序列化了一个C#protobuf-net
对象数组,并使用其消息结构的原始文件(struct_pb2.py)在Python中打开了该文件。
// The struct definition in C#
[ProtoContract]
public struct struct_tick
{
[ProtoMember(1)]
public uint[] arr_currentIndex;
[ProtoMember(2)]
public string[] arr_currentType;
[ProtoMember(3)]
public DateTime[] arr_currentTime; // <- for DateTime.Now objects
public struct_tick(byte size_index, byte size_type, byte size_time)
{
arr_currentIndex = new uint[size_index];
arr_currentType = new string[size_type];
arr_currentTime = new DateTime[size_time];
}
}
syntax = "proto3";
import "google/protobuf/timestamp.proto";
message struct_tick
{
repeated uint32 arr_currentIndex = 1;
repeated string arr_currentType = 2;
repeated google.protobuf.Timestamp arr_currentTime = 3;
}
它在Python中很好地打开,但是如何将C#日期时间对象正确转换为例如Python日期时间对象?使我感到困惑的是文件的datetime数组中显示的内容。
>>> struct = struct_pb2.struct_tick()
>>> with open(filename, "rb") as f:
>>> struct.ParseFromString(f.read())
>>> lst_time = [ time for time in struct.arr_currentTime]
>>> lst_time[:5]
[seconds: 1573571465
nanos: 335624000
, seconds: 1573571465
nanos: 335624000
, seconds: 1573571465
nanos: 335624000
, seconds: 1573571465
nanos: 335624000
, seconds: 1573571465
nanos: 335624000
]
>>> lst_time[-5:]
[seconds: 1573571465
nanos: 337636000
, seconds: 1573571465
nanos: 337636000
, seconds: 1573571465
nanos: 337636000
, seconds: 1573571465
nanos: 337636000
, seconds: 1573571465
nanos: 337636000
]
当我用C#读取同一文件时,第一个datetime对象代表21:59:39.7450630,而最后一个对象代表23:38:50.3848014,这意味着有将近2个小时的间隔。但是Python中显示的内容完全不同。
是因为在序列化时使用了DateTime
而在反序列化时使用了google.protobuf.Timestamp
?如何正确转换它们?
添加
这是接收数据并将其写入结构中的数组的回调函数:
static void Handler_Real(string szCode)
{
string code = REAL_TR.GetData("Out", "sym");
string currentType = REAL_TR.GetData("Out", "cg");
DateTime currentTime = DateTime.Now;
if (dic_codeToTRstructCounter[code] == arraySize)
{
dic_codeToTRstructCounter[code] = 0;
using (var fileStream = new FileStream(dic_codeToTRfilename[code], FileMode.Append))
{
Serializer.Serialize(fileStream, dic_codeToTRstruct[code]);
}
}
dic_codeToTRstruct[code].arr_currentIndex[dic_codeToTRstructCounter[code]] = dic_codeToTRCounter[code];
dic_codeToTRstruct[code].arr_currentType[dic_codeToTRstructCounter[code]] = currentType;
dic_codeToTRstruct[code].arr_currentTime[dic_codeToTRstructCounter[code]] = currentTime;
dic_codeToTRstructCounter[code] += 1;
dic_codeToTRCounter[code] += 1;
}
这就是我用C#读取文件的方式,效果很好。
using (var fileStream = new FileStream("file.bin", FileMode.Open))
{
struct_tick str = Serializer.Deserialize<struct_tick>(fileStream);
for (int startNum = 0; startNum < str.arr_currentIndex.Length; startNum++)
{
string str_print = string.Format("{0}, {1}, {3}", str.arr_currentIndex[startNum],
str.arr_currentType[startNum],
str.arr_currentTime[startNum].ToString("HH:mm:ss.fffffff"));
listBox1.Items.Add(str_print);
}
}
这就是我在Python中读取相同文件的方式
def convert_TRtoArray(filename):
struct = struct_pb2.struct_TR()
with open(filename, "rb") as f:
struct.ParseFromString(f.read())
lst_currentIndex = [ indexNum for indexNum in struct.arr_currentIndex ]
lst_currentType = [ type for type in struct.arr_currentType ]
lst_currentTime = [ time for time in struct.arr_currentTime ]
当我检查lst_currentTime
时,尽管C#程序已经接收了2个小时的数据,但是这些值与上面的一样。
答案 0 :(得分:1)
在像Protobuf这样的二进制可序列化流中使用DateTime并不是一种好习惯。最好将DateTime值转换为其他时间,例如纪元时间或什至字符串,然后对纪元时间或字符串值使用序列化。否则,您将无法正确还原该字段的内容。
答案 1 :(得分:1)
最简单的方法是使用:
[ProtoMember(..., DataFormat = DataFormat.WellKnown)]
您的DateTime
/ TimeSpan
使用情况;这使得protobuf-net使用“众所周知的”时间戳和持续时间布局而不是默认格式。它没有自动执行此操作的原因是,当protobuf-net首次为它们确定 some 布局时,它们(时间戳/持续时间)就不存在了。
但是,请注意,添加此功能是一项重大更改:布局不兼容。
如果需要使用旧版式,则会在bcl.proto
中对它们进行描述/定义。
或者,在protobuf-net v3中,您可以根据需要使用明确的时间戳/持续时间类型。
给出讨论(评论),坦率地说,您似乎没有正确填充数组,或者在上下文之间泄漏了该数组。在本地测试,看起来还不错-以下演示演示了它的工作原理(与您的struct_tick
一起使用),其中包括二进制数据的细分,其中显示了看起来正确的秒/纳米。
using ProtoBuf;
using System;
using System.IO;
class Program
{
static void Main()
{
var payload = new struct_tick(2, 2, 2);
payload.arr_currentIndex[0] = 12;
payload.arr_currentIndex[1] = 14;
payload.arr_currentType[0] = "abc";
payload.arr_currentType[1] = "def";
payload.arr_currentTime[0] = new DateTime(2019, 11, 12, 21, 59, 39, 745, DateTimeKind.Utc);
payload.arr_currentTime[1] = new DateTime(2019, 11, 12, 23, 38, 50, 385, DateTimeKind.Utc);
var ms = new MemoryStream();
Serializer.Serialize(ms, payload);
var hex = BitConverter.ToString(ms.GetBuffer(), 0, (int)ms.Length);
Console.WriteLine(hex);
ms.Position = 0;
var clone = Serializer.Deserialize<struct_tick>(ms);
foreach(var time in clone.arr_currentTime)
Console.WriteLine(time); // writes 12/11/2019 21:59:39 and 12/11/2019 23:38:50
// hex is 08-0C-08-0E-12-03-61-62-63-12-03-64-65-66-
// 1A-0C-08-CB-D6-AC-EE-05-10-C0-98-9F-E3-02-
// 1A-0C-08-8A-85-AD-EE-05-10-C0-C4-CA-B7-01
// 08-0C = field 1, 12
// 08-0E = field 1, 14
// 12-03-61-62-63 = field 2, "abc"
// 12-03-64-65-66 = field 2, "def"
// 1A-OC = field 3, length 12
// 08-CB-D6-AC-EE-05 = field 1, 1573595979 = seconds
// 10-C0-98-9F-E3-02 = field 2, 745000000 = nanos
// 1A-OC = field 3, length 12
// 08-8A-85-AD-EE-05 = field 1, 1573601930 = seconds
// 10-C0-C4-CA-B7-01 = field 2, 385000000 = nanos
}
}
[ProtoContract]
public struct struct_tick
{
[ProtoMember(1)]
public uint[] arr_currentIndex;
[ProtoMember(2)]
public string[] arr_currentType;
[ProtoMember(3, DataFormat = DataFormat.WellKnown)]
public DateTime[] arr_currentTime; // <- for DateTime.Now objects
public struct_tick(byte size_index, byte size_type, byte size_time)
{
arr_currentIndex = new uint[size_index];
arr_currentType = new string[size_type];
arr_currentTime = new DateTime[size_time];
}
}