3

我有一个具有挑战性的情况;我们将在 Mac、PC、iOS 和 Android 上安装程序,以接收传统格式的文件并解析这些文件中的数据。我们无法更改这些文件的创建方式。

这些文件是由一个 C++ 程序生成的,它用数字和字符串填充一个结构,然后把它写出来。这是一个经过消毒的版本。

struct MyObject {
String Kfkj(MAXKYS); 
String Oern(MAXKYS);
String Vdflj(MAXKYS, 9); 
int Muic;
int Tdfkj;
int VdfkAsdk;
int SsdjsdDsldsk; 
int Ndsoief; 
String TdflsajPdlj; 
String TdckjdfPas; 
String AdsfakjIdd;
int IdkfjdKasdkj;
int AsadkjaKadkja(MAXKYS); 
int Kasldsdkj;
bool Usadl;
String PsadkjOasdj(9); 
String PasdkjOsdkj;
};

如您所见,原语和字符串。

然后这是他们将其写入文件的方式:

MyInstance MyObject;
FileName = "C:\MyFile.ab2"
ofstream fout (FileName, ios::binary);
fout.write((char*)& MyInstance, sizeof(MyInstance));

我们没有选择翻译一次然后将文件分发到其他平台;我们必须在每一个不同的平台上翻译它,这就是我们必须合作的。我很感激有关 C++ 如何序列化数据的任何信息,因此我们知道如何解析文件。

编辑:解决方案

我从这里的多个答案中收到的反馈非常有帮助。使用它,我使用十六进制编辑器进行了广泛的分析并发现:

  • 元素一个接一个地进入文件
  • 在这种情况下,“字符串”以一个 int 开头,描述了该字符串的 int 后面有多少个字符。如果 String 不存在,它仍然具有值为 0 的 int。
  • 对于我看到的文件和机器,整数是两个字节,小端,并且大部分是无符号的(有一些是有符号的,只是为了让我保持警惕)
  • 布尔值是两个字节,显然 -1 (FF FF) 代表“真”

到目前为止,我们还没有遇到不同设备上不同填充或字节序的问题,但这些都是非常现实的问题。这些答案中熟练的注释和警告为我们提供了更多的弹药来尝试说服客户更改为更不脆弱的替代方案,例如 XML 或 JSON,以便跨平台在线传输数据。

至于那些询问开发人员是否被解雇的人......好吧,我们只是说他们的代码很旧,但经过多次对话,我们仍然无法说服他们写出 C++ 结构并尝试在不同平台上阅读它这不是一个好主意。

4

7 回答 7

4

数据文件的格式完全取决于编译 C++ 程序的编译器以及 String 类的定义。您可以依赖字段按照声明它们的顺序排列,在这种情况下,我认为您可以依赖一开始没有任何填充,但仅此而已。在这种情况下可能会帮助您的一些提示:-

  • 您没有给出您正在使用的 String 类的定义。如果是typedeffor std::string,那你就完蛋了,因为字符串的内容不在内存中。我假设你的 C++ 程序员正在使用一些特殊的本地缓冲区,在这种情况下,我猜你会发现对象的第一个字节是字符串,之后会有一些无用的填充。我希望该结构在开始时包含一个 int,告诉您其中有多少数据是有用的。
  • 您可能会发现这些int字段有四个字节长。
  • 您可能会发现该bool字段只有一个字节长,然后是三个字节的无用填充。只有一位,很可能是底部位,将被设置。

这就是我能提供给你的所有有用的猜测。在您的目标语言中,确保将整个文件作为与该语言中可用的字节数组最接近的内容读取,然后才使用语言功能将其转换为您的语言中正确的内容。不要尝试将其作为整数读取,因为如果您在与 C++ 程序具有不同字节序的平台上,那将不会让您进行字节交换。我建议还在文本编辑器中查看文件以对其进行逆向工程并帮助您找到每个字段的偏移量。

最后一条建议:考虑为那些认为这种“序列化”是个好主意的程序员或项目经理打印 P45(或粉色纸条,或您所在国家/地区的任何东西)。这种草率的工作在生死攸关的情况下可能是可以接受的,但它们严重地把你搞砸了,你会发现很难从中恢复过来。编写代码以读取这些文件不会那么难,如果它只是这样的一个结构,但保持它的可靠性将是一个痛苦的世界,而且他们实际上已经使自己无法安全地更改编译器或编译器版本.

于 2012-06-26T20:16:38.163 回答
4

你会遇到很多问题。

C++ 本身没有用于序列化数据的特定格式。它高度依赖于您运行的计算机体系结构/处理器。

允许编译器添加填充以帮助系统对齐。当我们说对齐时,我们基本上是指架构/处理器对数据位于特定字节边界上的亲和力。例如,某些处理器非常喜欢浮点数位于 4 或 8 字节边界 - 如果它们不这样做,处理器可能会运行得更慢或根本无法运行。

所以,你不能简单地知道你的系统神奇地添加了什么填充。

你可以做的是使用#pragma pack(1) / #pragma pack(0) 来阻止你的编译器填充你的数字。

PS:您还必须担心字节顺序。如果一台计算机以大端方式运行而一台以小端方式运行怎么办?他们将在没有转换的情况下以不同的方式解释字节。

简而言之,您要么必须修复生成文件的应用程序,以便它使用正确的序列化方案,要么您需要查看它在特定计算机上运行,​​查看它是如何编写文件的,并为每个目标平台编写一个翻译器(这很愚蠢)。

有趣的建议

如果您真的卡住了,请编写一个应用程序来监控您写入文件的文件夹。让应用程序获取文件(因为它在同一台 PC 上,它可以毫无问题地读取它们的格式)。让它以 XML 或其他一些真正的序列化格式写回文件并分发这些文件。

于 2012-06-26T20:08:11.350 回答
4

哇——太疯狂了。所以 String 对象不包含任何指针?不能——因为你声称这是工作代码。

无论如何,该代码没有进行任何序列化。它只是将结构写出来以完全按照它在内存中的布局方式进行归档。您遇到的唯一问题是,在某些平台上,整数类型(如 int)的填充和 sizeof 可能不同。

您必须找到整数类型的大小,并在新平台的读取器/写入器中使用该信息,以确保它们在旧平台上以相同的方式布局。

但是,您使用该代码确实存在风险。实际上,编译器更改可能会突然导致文件布局发生更改。

于 2012-06-26T20:10:26.590 回答
1

没有人指出我认为特别有问题的东西(也许是因为我被它咬了)。那个问题:数据成员bool Usadl;sizeof(bool) 跨平台、跨编译器,甚至跨同一编译器的不同版本。的常见值sizeof(bool)是 4 和 1。这会咬你。现在很难找到一台大端机器,非常非常难找到一台CHAR_BIT不是 8 或sizeof(int)不是 4 的计算机。对于sizeof(bool).

与其他人一致,Chad 的团队需要记录文件中记录的结构,然后确保生成文件的程序明确地写入此结构,包括元素大小、填充和字节顺序。不要依赖类布局来为您执行此操作。那只是自找麻烦。

于 2012-06-26T21:14:08.917 回答
1

在 ofstream 中写入不会序列化数据。此代码写入结构的原始内存内容,因为它是一个 char 字符串。根据你的编译器,它的版本、它的选项和它在内容上运行的系统将完全不同。甚至 char 的位数也允许在 c++ 实现之间更改。结构对象引用的数据不会被写入(忘记 std::string 的内容)。

如果您无法更改编写器代码。您必须知道对齐策略、基本类型的大小和数据表示。您将不得不分析手工生成的文件,例如使用像 http://www.physics.ohio-state.edu/~prewett/hexedit/这样的十六进制编辑器 ,并且可能查看您的编译器文档。

如果您可以更改编写器代码。使用适当的序列化,如 json、协议缓冲区或简单的 xml。

于 2012-06-26T20:08:14.567 回答
1

它的完成方式是,结构以原始形式写入文件。所以基本上你需要知道解析这个文件是你的结构的二进制布局。

基本上,这些字段是一个接一个,所以要读取一个 int,您只需读取 4 个字节并将其转换为一个 int 等。

字符串是一种特殊情况。从您的代码中不清楚此“String”类型是内联字符数组还是指向此类数组的指针。在第一种情况下,您需要知道每个字符串包含多少个字符,然后简单地按顺序读取该数量的字符。在第二种情况下,您将无法取回字符串,因为它不会被写入文件。指针对您毫无用处。

最后一个问题是结构是否被打包。由于您没有给出任何指示,默认情况下结构字段与 4 字节边界对齐,因此在您需要考虑的布尔字段之后可能会有空间。如果结构被打包,那么每个字段都直接在前一个字段之后。

所以,长话短说,用它的定义找出你的结构二进制布局,如果一切都失败了,用调试器在运行时检查内存,或者使用十六进制编辑器来研究输出文件。然后在某处写下该规范,这将为您提供您需要从文件中读取的内容。仅仅通过查看您提供的伪定义,不可能准确地知道该布局是什么。

于 2012-06-26T20:09:51.070 回答
0

最好的方法可能是使用 JSON,或者如果您想要更强大的解决方案,请使用Avro之类的解决方案。Avro 有一个C++ API和一个Java API,因此它涵盖了您遇到的大多数情况。

于 2012-06-26T20:07:57.823 回答