我目前正在使用BinaryWriter将 C# 类序列化为二进制流。
我取出类的每个元素并使用 BinaryWriter 将其写出来。这很好用,因为读取此二进制文件的 C++ 应用程序支持打包结构,因此可以直接加载二进制文件。
现在我有一个处理对齐的请求,因为弹出了一个不支持打包结构的新应用程序。转换 C# 类并将其导出为二进制文件的最佳方法是什么,同时记住 2 字节和 4 字节对齐?
用户可以选择对齐方式。
我目前正在使用BinaryWriter将 C# 类序列化为二进制流。
我取出类的每个元素并使用 BinaryWriter 将其写出来。这很好用,因为读取此二进制文件的 C++ 应用程序支持打包结构,因此可以直接加载二进制文件。
现在我有一个处理对齐的请求,因为弹出了一个不支持打包结构的新应用程序。转换 C# 类并将其导出为二进制文件的最佳方法是什么,同时记住 2 字节和 4 字节对齐?
用户可以选择对齐方式。
在任何语言对齐的对象序列化中都应该不是问题,因为您提前知道正在写入和读取多少数据。
例如,采用以下结构:
struct data
{
char c;
unsigned int i;
double d;
}
根据编译器如何进行内存布局,此结构在内存中的大小可以是 13 到 20(压缩 - 32 位对齐)之间的任何位置,但这就是内存布局。就磁盘布局而言,您(假设是二进制)总是要写:
因此,当另一方读入它时,无论是 Python 还是 C# 或其他任何应该执行以下操作的东西:
这几乎是规范的解决方案。如果语言和体系结构之间的可移植性是一个问题,您永远不应该依赖任何语言的结构的大规模块写入。
此外,上面没有考虑字节序,这是序列化整数时需要考虑的问题 - 通常就像转换为网络字节顺序然后返回一样简单,分别用于写入和读取。
您可能要考虑不将struct
s 覆盖到内存缓冲区上。只需在反序列化步骤中读取字节即可。就像是:
struct A {
uint8_t aByte;
uint32_t aDWord;
uint16_t aWord;
};
void serialize(FILE* fp, A const& aStruct) {
fwrite(&aStruct.aByte, sizeof(aStruct.aByte), 1, fp);
fwrite(&aStruct.aDWord, sizeof(aStruct.aDWord), 1, fp);
fwrite(&aStruct.aWord, sizeof(aStruct.aWord), 1, fp);
}
void deserialize(FILE* fp, A& aStruct) {
fread(&aStruct.aByte, sizeof(aStruct.aByte), 1, fp);
fread(&aStruct.aDWord, sizeof(aStruct.aDWord), 1, fp);
fread(&aStruct.aWord, sizeof(aStruct.aWord), 1, fp);
}
代替:
void serialise(FILE* fp, A const& aStruct) {
fwrite(&aStruct, sizeof(aStruct), 1, fp);
}
void deserialise(FILE* fp, A& aStruct) {
fread(&aStruct, sizeof(aStruct), 1, fp);
}
第一个示例不依赖于第二个示例所在的结构打包规则。我建议使用不是的那个。大多数语言(不确定 C#)为您提供了一些读取和写入原始字节的方法,因此所有的序列化/反序列化成员而不是单个内存块,并且打包/填充问题消失了。
考虑在编写器端使用 C++ 库。如果读者端受到限制,请在非托管 C++ 中创建具有相同内存布局的结构并将它们转储到二进制文件中。您可能必须使用 #pragma pack(1) 禁用默认打包并在结构元素之间放置一些 char[] 虚拟对象以类似于阅读器端的对齐方式。不要忘记字节序。然后只需使用 DllImport 或通过 C++/CLI 调用编写器库(这是个人喜好问题)。