如何在Java中阅读Delphi记录结构

Ray*_*ayz 2 java delphi structure file record

我有一个由Delphi记录组成的二进制文件.记录看起来像:

TRMapFileHeader = record
    FileType: String[8];
    Points: Int64;
    Objects: Int64;
    Text: Int64;
    ObjLayers: byte;
    TextLayers: byte;
  end;
Run Code Online (Sandbox Code Playgroud)

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

DataInputStream file = new DataInputStream(new FileInputStream(filename))
Run Code Online (Sandbox Code Playgroud)

然后我试图读取数据:

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());
Run Code Online (Sandbox Code Playgroud)

而且我有

日食输出

而不是正确的数据:

RMF
441434
80457
14186
11
4
Run Code Online (Sandbox Code Playgroud)

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

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());
Run Code Online (Sandbox Code Playgroud)

给出下一个输出: Eclipse输出.第一个字节等于3,然后是3个字符,然后是36个字节,然后是记录的最后2个参数

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

Dav*_*nan 6

Delphi类型String[8]是一个短字符串.它的实现包含一个包含字符串长度的额外前导字节.所以,大小String[8]是9个字节.

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

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

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

Offset  Length  Field
 0      9       FileType, 1 byte length, 8 bytes payload
 9      7       <padding>
16      8       Points
24      8       Objects
32      8       Text
40      1       ObjLayers
41      1       TextLayers
42      6       <padding>

由于记录末尾的填充,记录的总长度为48.这很重要,因为如果你没有跳过记录末尾的填充,那么你将在错误的位置读取文件中的下一个内容.

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

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

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

6538107356104884224
5276531012929585152
7653586091739447296

并转换为十六进制我们有:

5ABC060000000000
493A010000000000
6A37000000000000

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

6BC5A
13A49
376A

以十进制表示

441434
80457
14186

这些都是你想要的价值观.哎呀,我们最终到了那里!