简体   繁体   English

在Delphi上读取从JAVA生成的二进制文件

[英]Read binary file generated from JAVA on Delphi

I've generated a binary file from JAVA: 我从JAVA生成了一个二进制文件:

RandomAccessFile out = new RandomAccessFile(file, "rwd");
out.writeLong(calendar.getTimeInMillis());
out.writeDouble(double1);
out.writeDouble(double2);
out.writeDouble(double3);

How can I read this information from Delphi 2007? 我如何从Delphi 2007中读取此信息?

I've tried something like this: 我尝试过这样的事情:

type TData = record
  time: TDateTime;
  double1: double;
  double2: double;
  double3: double;
end;
var
   data: TData;
   F : file of TData;
begin
  AssignFile(F,fileName) ;
  Reset(F) ;
  Read (F, data);
...

but the values of time, double1, double2 and double3 are completly different. 但是time,double1,double2和double3的值完全不同。

calendar.getTimeInMillis() returns a long. calendar.getTimeInMillis()返回一个long。

Your best bet if using delphi 7+ is a UInt64 to match java long 如果使用delphi 7+是一个匹配java long的UInt64,那么你最好的选择

See here for java primitive datatypes descriptions and try to match to delphi datatypes 请参阅此处了解java原始数据类型描述并尝试匹配delphi数据类型

edit : As David Heffernan noticed you'll also have to convert endianness 编辑:正如David Heffernan注意到你还必须转换字节序

There are two issues here. 这里有两个问题。

  1. Java is big endian and Delphi is little endian. Java是大端,Delphi是小端。
  2. The Java code writes the date as a 64 bit integer. Java代码将日期写为64位整数。 The Delphi code reads a floating point TDateTime . Delphi代码读取一个浮点TDateTime

Assuming you are going to change the Delphi code rather than the Java code, here's what you would do to bring the two sides together: 假设您要更改Delphi代码而不是Java代码,这是将两者结合在一起的方法:

  • Find or write some helper utilities to deal with the endian issue. 查找或编写一些帮助程序来处理字节序问题。 Convert from big endian to little endian as soon as you read the data. 读取数据后立即从大端转换为小端。
  • Read the date as a 64 bit integer. 将日期读为64位整数。 Then you need to work out what the Java epoch is and convert from milliseconds since the Java epoch into a Delphi TDateTime which measures days elapsed since the Delphi epoch. 然后你需要弄清楚Java纪元是什么,并从Java纪元以来的毫秒转换成Delphi TDateTime ,它测量自Delphi纪元以来的几天。

Dealing with endianness is simple enough, albeit rather tiresome. 处理字节序很简单,尽管很烦人。

The time conversion is perhaps a little more involved. 时间转换可能涉及更多一点。 The key information is that the Java epoch is the same as the Unix epoch. 关键信息是Java时代与Unix时代相同。 So a function that converts Unix time to Delphi TDateTime is all you need. 因此,只需要将Unix时间转换为Delphi TDateTime的函数。 Fortunately the Delphi RTL supplies the very function. 幸运的是,Delphi RTL提供了很好的功能。 It's in the DateUtils unit and is named UnixToDateTime . 它位于DateUtils单元中,名为UnixToDateTime Note that UnixToDateTime receives a Unix time measured in seconds so you'll need to divide your value in milliseconds by 1000 . 请注意, UnixToDateTime接收以秒为单位的Unix时间,因此您需要将以毫秒为单位的值除以1000

One other point that I would make is that the Java code writes the data out with no gaps between fields. 我要说的另一点是,Java代码将数据写入字段之间没有任何间隙。 But the Delphi code uses an aligned record. 但Delphi代码使用对齐记录。 Now, since all the members are the same size, there is no padding in this case. 现在,由于所有成员的大小都相同,因此在这种情况下无需填充。 But it's something to watch out for. 但值得注意的是。 If I were you I would not be using legacy Pascal I/O to read this. 如果我是你,我不会使用传统的Pascal I / O来阅读它。 I'd use a binary reader class that operates in a similar way to your Java writer. 我将使用二进制阅读器类,其操作方式与Java编写器类似。 And I'd use that reader to read in the fields one at a time. 我会用那个读者一次一个地阅读这些字段。

There may be something to be gained from finding (or writing) a reader class that handles the endian conversion for you. 找到(或编写)一个为您处理字节序转换的阅读器类,可能会有所收获。

Finally the solution is: 最后的解决方案是:

function Swap8ToDouble(A:double): double;
var
  hold:double;
asm
  mov edx,dword ptr[A]
  mov ecx,dword ptr[A+4]
  bswap edx
  bswap ecx
  mov dword ptr [hold],ecx
  mov dword ptr [hold+4],edx
  fld hold;
end;

function Int64Swap(A: int64): int64;
asm
  mov edx,dword ptr [A]
  mov eax,dword ptr [A+4]
  bswap edx
  bswap eax
end;


type TData = record
    time: Int64;
    double1: double;
    double2: double;
    double3: double;
end;

...

data.time := UnixToDateTime(Int64Swap(data.time) div 1000);
data.double1 := Swap8ToDouble(data.double1);
data.double2 := Swap8ToDouble(data.double2);
data.double3 := Swap8ToDouble(data.double3);

Your primary logic is OK but, since you're writing and reading binary data, you have to maintain DataType compatibility between the writer and the reader, and you also have to take endianness of writer and reader. 您的主要逻辑是可以的,但是,由于要写入和读取二进制数据,因此必须维护写入器和读取器之间的数据类型兼容性,并且还必须考虑写入器和读取器的字节顺序。

The java calendar.getTimeInMillis() returns a long , the Delphi equivalent is Int64 , whereas Double is equivalent in both (64 bit IEEE), so your record should look like this: java calendar.getTimeInMillis()返回一个long ,Delphi等效为Int64 ,而Double等效于这两个(64位IEEE),因此您的记录应如下所示:

type TData = record
  Millis: Int64;
  double1: double;
  double2: double;
  double3: double;
end;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM