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

end*_*nge 8 delphi delphi-7 endianness

我很困扰。我需要读取二进制文件(Applied Biotechnology aka ABIF 的 .fsa 扩展名、FASTA 文件),但在读取有符号整数时遇到了问题。我正在根据本手册做所有事情https://drive.google.com/file/d/1zL-r6eoTzFIeYDwH5L8nux2lIlsRx3CK/view?usp=sharing 所以,例如,让我们看看文件https头中的 fDataSize 字段: //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 指令。但在这种情况下,它们以一种错误的方式工作,特别是:Swap,应用于 16809994,返回 16779904 或 00000001 00000000 00001010 10000000,并且 BSWAP 指令将 16809996, 01001001001001001001001001001001001001001001001001001001001

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

更新。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;
Run Code Online (Sandbox Code Playgroud)

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

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);
...
Run Code Online (Sandbox Code Playgroud)

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

在此处输入图片说明

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

查看文件开头的二进制结构(fDataSize 字节高亮显示): 在此处输入图片说明

And*_*and 11

问题与字节顺序无关,而是与 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;
Run Code Online (Sandbox Code Playgroud)

并且您希望此记录覆盖文件中的字节,并带有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;
Run Code Online (Sandbox Code Playgroud)

然后字段将位于预期位置。

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

最好是BSWAP指令。