首页 > 解决方案 > 如果 Windows/Delphi/IDE 暗示小端顺序,我如何从大端二进制文件中读取整数?

问题描述

我很困扰。我需要读取二进制文件(Applied Biotechnology 的 .fsa 扩展名,又名 ABIF、FASTA 文件),但在读取有符号整数时遇到了问题。我正在根据 手册https://drive.google.com/file/d/1zL-r6eoTzFIeYDwH5L8nux2lIlsRx3CK/view?usp=sharing //drive.google.com/file/d/1rrL01B_gzgBw28knvFit6hUIA5jcCDry/view?usp=sharing

我知道它应该是 2688(根据手册,它是一个 32 位的有符号整数),即 00000000 00000000 00001010 10000000 的二进制形式。实际上,当我将这 32 位作为 4 字节数组读取时,我得到 [0, 0, 10, -128],这与二进制形式完全相同。

但是,如果我将其读取为整数,则结果为 16809994,即 00000001 00000000 10000000 00001010 位。

正如我从多个论坛了解的那样,他们使用 Swap 和 htonl 函数将整数从小端顺序转换为大端顺序。他们还建议对 32 位整数使用 BSWAP EAX 指令。但是在这种情况下,它们以一种错误的方式工作,具体来说:交换,应用于 16809994,返回 16779904 或 00000001 00000000 00001010 10000000,BSWAP 指令将 16809994 转换为 176160769,即 0000100000000000000

正如我们所看到的,内置函数做的事情与我需要的不同。交换可能会返回正确的结果,但由于某种原因,将这些位作为整数读取会更改最左边的字节。那么,出了什么问题,我该怎么办?

更新。1 为了存储标题数据,我使用以下记录:

type
  TFasMainHeader = record
    fFrmt        : array[1..4]  of ansiChar;
    fVersion     : Word;
    fDir         : array[1..4] of ansiChar;
    fNumber      : array[1..4]  of Byte; //
    fElType      : Word;
    fElSize      : Word;
    fNumEls      : array[1..4]  of Byte; //
    fDataSize    : Integer;
    fDataOffset  : Integer;
    fDO : word;
    fDataHandle  : array[1..98]  of Byte;
  end;

然后单击按钮,我执行以下操作:

aFileStream.Read(fas_main_header, SizeOf(TFasMainHeader));
with fas_main_header do begin
    if fFrmt <> 'ABIF' then raise Exception.Create('Not an ABIF file!');
    fVersion := Swap(fVersion);
    fElType := Swap(fElType);
    fElSize := Swap(fElSize);
...

接下来我需要以正确的方式交换Int32变量,但是此时fDataSize例如是16809994。调试时详细查看记录的状态:

在此处输入图像描述

这对我来说没有意义,因为 fDataSize 值的二进制表示中不应该有一位(它也会破坏 BSWAP 结果)。

查看文件开头的二进制结构(fDataSize 字节被突出显示): 在此处输入图像描述

标签: delphidelphi-7endianness

解决方案


该问题与字节顺序无关,而是与 Delphi记录有关。

你有

type
  TFasMainHeader = record
    fFrmt        : array[1..4]  of ansiChar;
    fVersion     : Word;
    fDir         : array[1..4] of ansiChar;
    fNumber      : array[1..4]  of Byte; //
    fElType      : Word;
    fElSize      : Word;
    fNumEls      : array[1..4]  of Byte; //
    fDataSize    : Integer;
    fDataOffset  : Integer;
    fDO : word;
    fDataHandle  : array[1..98]  of Byte;
  end;

并且您希望此记录覆盖文件中的字节,并带有fDataSize"on top of" 00 00 0A 80

但是 Delphi 编译器会在记录的字段之间添加填充以使它们正确对齐。因此,您fDataSize将不会处于正确的偏移量。

要解决此问题,请使用packed关键字:

type
  TFasMainHeader = packed record
    fFrmt        : array[1..4]  of ansiChar;
    fVersion     : Word;
    fDir         : array[1..4] of ansiChar;
    fNumber      : array[1..4]  of Byte; //
    fElType      : Word;
    fElSize      : Word;
    fNumEls      : array[1..4]  of Byte; //
    fDataSize    : Integer;
    fDataOffset  : Integer;
    fDO : word;
    fDataHandle  : array[1..98]  of Byte;
  end;

然后这些字段将位于预期的位置。

然后——当然——你可以使用任何你喜欢的方法来交换字节顺序。

最好是BSWAP指令。


推荐阅读