2

I've generated a binary file from 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?

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.

1
  • Does it HAVE to be a binary file? You could simplify things greatly with, say, xml. Commented Mar 26, 2013 at 17:53

4 Answers 4

3

calendar.getTimeInMillis() returns a long.

Your best bet if using delphi 7+ is a UInt64 to match java long

See here for java primitive datatypes descriptions and try to match to delphi datatypes

edit : As David Heffernan noticed you'll also have to convert endianness

Sign up to request clarification or add additional context in comments.

Comments

2

There are two issues here.

  1. Java is big endian and Delphi is little endian.
  2. The Java code writes the date as a 64 bit integer. The Delphi code reads a floating point 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:

  • 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. 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.

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. So a function that converts Unix time to Delphi TDateTime is all you need. Fortunately the Delphi RTL supplies the very function. It's in the DateUtils unit and is named UnixToDateTime. Note that UnixToDateTime receives a Unix time measured in seconds so you'll need to divide your value in milliseconds by 1000.

One other point that I would make is that the Java code writes the data out with no gaps between fields. But the Delphi code uses an aligned record. 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. I'd use a binary reader class that operates in a similar way to your Java writer. 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.

Comments

2

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);

1 Comment

The other problem with your code is that a signaling nan will lead to an exception being raised. You need to swap bytes without loading into the FPU. Your code is not portable and restricted to x86.
0

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:

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

1 Comment

I would suggest making the record packed because it's written without alignment to file (it doesn't really matter now because all record members are 8 bytes long but new values may be added in the future)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.