我有一个由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());
我已经
了而不是正确的数据:
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());
所以我想知道如何阅读这种记录
答案 0 :(得分:6)
Delphi类型String[8]
是short string。它的实现包含一个包含字符串长度的额外前导字节。因此,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
这些是你想要的价值观。哎呀,我们最终到了那里!