【问题标题】:How to read Delphi record structure in Java如何在 Java 中读取 Delphi 记录结构
【发布时间】:2013-10-27 15:15:02
【问题描述】:

我有一个包含 Delphi 记录的二进制文件。 记录如下:

TRMapFileHeader = record
    FileType: String[8];
    Points: Int64;
    Objects: Int64;
    Text: Int64;
    ObjLayers: byte;
    TextLayers: byte;
  end;

我想用 Java 读取这个文件。我打开了文件:

DataInputStream file = new DataInputStream(new FileInputStream(filename))

然后我尝试读取数据:

for(int i = 0; i<8; i++)
    System.out.print((char)file.readByte());
System.out.println();
System.out.println(file.readLong());
System.out.println(file.readLong());
System.out.println(file.readLong());
System.out.println(file.readByte());
System.out.println(file.readByte());

我有

eclipse output

而不是正确的数据:

RMF
441434
80457
14186
11
4

我玩了不同的阅读方式,发现了下一个:

System.out.println(file.readByte());
for(int i = 0; i<3; i++)
    System.out.print((char)file.readByte());

for(int i = 0; i<36; i++)
    file.readByte();

System.out.println();
System.out.println(file.readByte());
System.out.println(file.readByte());

给出下一个输出: Eclipse output. First byte equals 3, then goes 3 characters, then 36 bytes and then last 2 parameters of record

所以我想知道如何阅读这种记录

【问题讨论】:

  • 考虑在 Delphi 中使用打包记录,这样您就不必处理对齐问题。
  • 为什么要使用打包记录?如果您在其他地方重复使用记录,这将导致其他地方损坏。
  • @MarcusAdams 嗯,不确定。使用记录来二进制 blit 数据是如此 1970 年代!例如,如今 BinaryWriter/BinaryReader 会更有意义。打包记录只会让性能很差。
  • 我想知道为什么不直接使用任何十六进制编辑器/查看器并使用试验和错误来解析文件,然后在 java 中重新创建解析
  • @Arioch'The 好吧,我想如果您无法从第一原则中解决问题,您可能会采取反复试验。但是你怎么能确定你做对了。如果您掷硬币得到 H,T,H,T,H,T,您可能会得出结论,掷硬币会产生交替序列。

标签: java file delphi structure record


【解决方案1】:

德尔福类型String[8]short string。它的实现包含一个包含字符串长度的额外前导字节。所以,String[8] 的大小是 9 个字节。

您需要读取第一个字节来查找长度,然后读取接下来的 8 个字节来查找有效负载。请记住,第一个字节告诉您后续 8 个字节中有多少是有意义的。

另一件需要注意的是对齐。如问题中所述,记录似乎是一致的。它是否取决于 Delphi 编译器设置。 Delphi 编译器可能被指示打包记录。

假设不是。换句话说,让我们假设记录是对齐的。为了使字段正确对齐,Int64 字段将在 8 字节边界上对齐。这意味着记录的布局将如下所示:

偏移长度字段 0 9 文件类型,1 字节长度,8 字节有效负载 9 7 16 8 分 24 8 个对象 32 8 文本 40 1 个对象图层 41 1 文本图层 42 6

由于记录末尾的填充,记录的总长度为 48。这很重要,因为如果您不跳过记录末尾的填充,那么您将在错误的位置读取文件中接下来的内容。

粗略检查您的输出将表明记录确实是对齐的而不是打包的。您的第二个代码块读取 40 个字节,然后接下来的两个字节(偏移量 41 和 42)是 11 和 4,与我上面的表格相匹配。

最后要注意的一点是,生成这些文件的 Delphi 很可能使用小端整数。 Java 是大端(我相信),因此您需要对整数字段执行一点到大端的转换。例如使用java.nio.ByteBuffer

让我们看看这个假设。您声明您阅读的三个 long 具有以下值:

6538107356104884224 5276531012929585152 7653586091739447296

并转换为十六进制:

5ABC060000000000 493A010000000000 6A37000000000000

让我们反转字节(跳过前导零字节):

6BC5A 13A49 376A

十进制是

441434 80457 14186

这些是您想要的值。呼,我们终于到了!

【讨论】:

  • System.out.println(file.readByte()); for(int i = 0; i
  • 我读了 1 个字节,然后是 8,然后是 7(填充),然后是 3 次 LongInt(3 x 8 字节)和最后 2 个字节(它们是正确的 '11' 和 '4')但是那些 3 LongInt 值错误。
  • 好的,答案已更新。唯一合理的解释是字节序。我猜 Java 是大端的。
  • 你能解释一下填充吗?为什么是 7 和 6,为什么它们在原处?或者你能分享一下文献链接吗?
  • 简单来说,添加了填充,以便字段从偏移量开始,该偏移量是字段类型对齐的精确倍数。 Int64 的对齐方式为 8,因此需要放置在偏移量 0 或 8 或 16 处,依此类推。阅读有关填充和对齐的 Wikipedia 主题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-20
  • 2012-07-28
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
相关资源
最近更新 更多