使用Inno Setup从文件读取所需位置的字节 [英] Read bytes from file at desired position with Inno Setup

查看:157
本文介绍了使用Inno Setup从文件读取所需位置的字节的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想打开一个50 MB的二进制文件,仅读取最后4个字节,然后出于某种目的将其转换为字符串.

I want to open a 50 MB binary file and read only the last 4 bytes and convert it to string for some purpose.

我发现现在唯一的方法是使用LoadStringFromFile,将文件完全加载到内存中,然后复制最后4个字节,但是这种方法非常慢,因为二进制文件很重.

The only way I found to do it now is using LoadStringFromFile, to load the file entirely on the memory then copy that last 4 bytes, however this method is very slow because the binary file is heavy.

在Inno Setup脚本中还有更好的方法吗?

Is there any better way to do it in Inno Setup script?

更新:

这是我根据Martin Prikryl的答案编辑的最终工作功能

This is a final working function that I edited from Martin Prikryl's answer

function readlast4byte() : AnsiString;
var
  Stream: TFileStream;
  Buffer: string;
  Count: Integer;
  Index: Integer;
begin
  Count := 4;

  Stream := TFileStream.Create('C:\test.txt', fmOpenRead);

  try
    Stream.Seek(-Count, soFromEnd);

    SetLength(Buffer, 1);
    SetLength(Result, Count);
    for Index := 1 to Count do
    begin
      Stream.ReadBuffer(Buffer, 1);
      Result[Index] := Chr(Ord(Buffer[1]))  ;
    end;
  finally
    Stream.Free;
  end;
end;


更新2:

这也是TLama编写的另一个出色的工作功能,也应标记为答案:

Also this is another great working function that written by TLama and should mark as answer too:

[Code]
#IFNDEF Unicode
  #DEFINE CharSize 1
#ELSE
  #DEFINE CharSize 2
#ENDIF

type
  TSeekOrigin = (
    soBeginning,
    soCurrent,
    soEnd
  );

#IFDEF UNICODE
function BufferToAnsi(const Buffer: string): AnsiString;
var
  W: Word;
  I: Integer;
begin
  SetLength(Result, Length(Buffer) * 2);
  for I := 1 to Length(Buffer) do
  begin
    W := Ord(Buffer[I]);
    Result[(I * 2)] := Chr(W shr 8); // high byte
    Result[(I * 2) - 1] := Chr(Byte(W)); // low byte
  end;
end;
#ENDIF

function ReadStringFromFile(const FileName: string; Origin: TSeekOrigin; Offset, Length: Integer;
  var S: AnsiString): Boolean;
var
  Buffer: string;
  Stream: TFileStream;
begin
  Result := True;
  try
    Stream := TFileStream.Create(FileName, fmOpenRead);
    try
      Stream.Seek(Offset, Ord(Origin));
      SetLength(Buffer, Length div {#CharSize});
      Stream.ReadBuffer(Buffer, Length);
      #IFNDEF UNICODE
        S := Buffer;
      #ELSE
        S := BufferToAnsi(Buffer);
      #ENDIF
    finally
      Stream.Free;
    end;
  except
    Result := False;
  end;

    end;

推荐答案

您可以使用TFileStream 使用.Seek(-4, soFromEnd)属性来查找指向所需位置的读取指针.

Use the .Seek(-4, soFromEnd) property to seek the read pointer to the desired position.

一个复杂的问题是TStream使用的字符不是字节,因此您必须将读回的Unicode字符串转换为字节.

A complication is that the TStream works with characters not bytes, so you have to convert Unicode strings that your read back to bytes.

仅读取四个字节时,逐字节读取将更容易,从而防止了任何多字节转换:

When reading just four bytes, it's way easier to read byte by byte, preventing any multibyte conversions:

procedure ReadFileEnd;
var
  Stream: TFileStream;
  Buffer: string;
  Count: Integer;
  Index: Integer;
begin
  Count := 4;

  Stream := TFileStream.Create('my_binary_file.dat', fmOpenRead);

  try
    Stream.Seek(-Count, soFromEnd);

    SetLength(Buffer, 1);
    for Index := 1 to Count do
    begin
      Stream.ReadBuffer(Buffer, 1);
      Log(Format('Byte %2.2x: %2.2x', [Index, Ord(Buffer[1])]));
    end;
  finally
    Stream.Free;
  end;
end;


这是@TLama提供的通用替代方法,可以有效地处理任意大读量:
https://pastebin.com/nzGEdXVj


Here's a generic alternative from by @TLama that efficiently works for arbitrary large read:
https://pastebin.com/nzGEdXVj

顺便说一句,对我来说 LoadStringFromFile函数似乎是足以加载50 MB文件的效率.仅需40毫秒.

By the way, for me LoadStringFromFile function seems to be efficient enough to load 50 MB file. It takes only 40 ms.

这篇关于使用Inno Setup从文件读取所需位置的字节的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆