【问题标题】:Read binary file generated from JAVA on Delphi在 Delphi 上读取 JAVA 生成的二进制文件
【发布时间】:2013-03-26 14:54:14
【问题描述】:

我已经从 JAVA 生成了一个二进制文件:

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

如何从 Delphi 2007 中读取此信息?

我尝试过这样的事情:

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

但是time、double1、double2和double3的值是完全不同的。

【问题讨论】:

  • 它必须是二进制文件吗?你可以用 xml 来大大简化事情。

标签: java delphi type-conversion


【解决方案1】:

calendar.getTimeInMillis() 返回一个 long。

如果使用 delphi 7+,你最好的选择是 UInt64 来匹配 java long

请参阅here 了解 java 原始数据类型描述并尝试匹配 delphi 数据类型

编辑:正如 David Heffernan 注意到的那样,您还必须转换字节顺序

【讨论】:

    【解决方案2】:

    这里有两个问题。

    1. Java 是大端,Delphi 是小端。
    2. Java 代码将日期写入为 64 位整数。 Delphi 代码读取浮点 TDateTime

    假设您要更改 Delphi 代码而不是 Java 代码,下面是您将两者结合在一起的方法:

    • 查找或编写一些帮助实用程序来处理字节序问题。读取数据后立即从大端转换为小端。
    • 将日期读取为 64 位整数。然后,您需要计算出 Java 纪元是什么,并将自 Java 纪元以来的毫秒数转换为 Delphi TDateTime,它测量自 Delphi 纪元以来经过的天数。

    处理字节序很简单,尽管相当烦人。

    时间转换可能涉及更多。关键信息是 Java 纪元与 Unix 纪元相同。因此,您只需要一个将 Unix 时间转换为 Delphi TDateTime 的函数。幸运的是,Delphi RTL 提供了这个功能。它位于DateUtils 单元中,名为UnixToDateTime。请注意,UnixToDateTime 接收以秒为单位的 Unix 时间,因此您需要将您的值(以毫秒为单位)除以 1000

    我要说明的另一点是,Java 代码将数据写出,字段之间没有间隙。但是 Delphi 代码使用对齐的记录。现在,由于所有成员的大小相同,因此在这种情况下没有填充。但这是需要注意的。如果我是你,我不会使用传统的 Pascal I/O 来阅读这篇文章。我会使用一个二进制读取器类,它的操作方式与您的 Java 编写器类似。我会使用那个阅读器一次阅读一个字段。

    找到(或编写)一个为您处理字节序转换的阅读器类可能会有所收获。

    【讨论】:

      【解决方案3】:

      最后的解决办法是:

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

      【讨论】:

      • 我认为您不应该使用 ASM 来实现字节序。顺便说一句,由于您是新手,请接受您认为的最佳答案。看到这个:meta.stackexchange.com/questions/5234/…
      • 您的代码的另一个问题是信号 nan 会导致引发异常。您需要在不加载到 FPU 的情况下交换字节。您的代码不可移植且仅限于 x86。
      【解决方案4】:

      您的主要逻辑是可以的,但是由于您正在写入和读取二进制数据,因此您必须保持写入器和读取器之间的 DataType 兼容性,并且您还必须采用写入器和读取器的字节序。

      java calendar.getTimeInMillis() 返回一个long,Delphi 等价物是Int64,而 Double 在两者中是等价的(64 位 IEEE),所以你的记录应该是这样的:

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

      【讨论】:

      • 我建议将记录打包,因为它是在没有与文件对齐​​的情况下写入的(现在并不重要,因为所有记录成员都是 8 字节长,但将来可能会添加新值)跨度>
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-26
      • 2011-07-25
      • 1970-01-01
      • 2012-07-08
      • 2015-07-27
      相关资源
      最近更新 更多