1

我目前正在使用BinaryWriter将 C# 类序列化为二进制流。

我取出类的每个元素并使用 BinaryWriter 将其写出来。这很好用,因为读取此二进制文件的 C++ 应用程序支持打包结构,因此可以直接加载二进制文件。

现在我有一个处理对齐的请求,因为弹出了一个不支持打包结构的新应用程序。转换 C# 类并将其导出为二进制文件的最佳方法是什么,同时记住 2 字节和 4 字节对齐?

用户可以选择对齐方式。

4

3 回答 3

3

在任何语言对齐的对象序列化中都应该不是问题,因为您提前知道正在写入和读取多少数据。

例如,采用以下结构:

struct data
{
   char c;
   unsigned int i;
   double d;
}

根据编译器如何进行内存布局,此结构在内存中的大小可以是 13 到 20(压缩 - 32 位对齐)之间的任何位置,但这就是内存布局。就磁盘布局而言,您(假设是二进制)总是要写:

  1. 为 c 写 1 个字节
  2. 为 i 写 4 个字节
  3. 为 d 写 8 个字节

因此,当另一方读入它时,无论是 Python 还是 C# 或其他任何应该执行以下操作的东西:

  1. 读取 1 个字节并转换为内部字符表示
  2. 读取 4 个字节并转换为内部无符号整数表示(请记住,如果在 java 中没有无符号整数)
  3. 读取 8 个字节并转换为内部实数或浮点表示

这几乎是规范的解决方案。如果语言和体系结构之间的可移植性是一个问题,您永远不应该依赖任何语言的结构的大规模块写入。

此外,上面没有考虑字节序,这是序列化整数时需要考虑的问题 - 通常就像转换为网络字节顺序然后返回一样简单,分别用于写入和读取。

于 2009-09-04T19:56:09.173 回答
2

您可能要考虑不将structs 覆盖到内存缓冲区上。只需在反序列化步骤中读取字节即可。就像是:

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#)为您提供了一些读取和写入原始字节的方法,因此所有的序列化/反序列化成员而不是单个内存块,并且打包/填充问题消失了。

于 2009-09-04T19:45:36.323 回答
0

考虑在编写器端使用 C++ 库。如果读者端受到限制,请在非托管 C++ 中创建具有相同内存布局的结构并将它们转储到二进制文件中。您可能必须使用 #pragma pack(1) 禁用默认打包并在结构元素之间放置一些 char[] 虚拟对象以类似于阅读器端的对齐方式。不要忘记字节序。然后只需使用 DllImport 或通过 C++/CLI 调用编写器库(这是个人喜好问题)。

于 2009-09-05T20:04:13.757 回答