2015-07-05 6 views
2

Я хочу открыть двоичный файл 50 МБ и читать только последние 4 байта и преобразовывать его в строку для какой-либо цели.Чтение байтов из файла в нужном месте с помощью Inno Setup

Единственный способ, которым я нашел это сделать, это использовать LoadStringFromFile, чтобы загрузить файл целиком в память, а затем скопировать последние 4 байта, однако этот метод очень медленный, потому что бинарный файл тяжелый.

Есть ли лучший способ сделать это в скрипте Inno Setup?

Update:


Это окончательная рабочая функция, я редактировал от ответа Мартина Přikryl в

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; 

Update 2:

Кроме того, эторугой большой рабочий функция, которая написана TLama и должен пометить как ответ тоже:

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

ответ

3

Вы можете использовать TFileStreamsupport class:

TStream = class(TObject) 
    function Read(Buffer: String; Count: Longint): Longint; 
    function Write(Buffer: String; Count: Longint): Longint; 
    function Seek(Offset: Longint; Origin: Word): Longint; 
    procedure ReadBuffer(Buffer: String; Count: Longint); 
    procedure WriteBuffer(Buffer: String; Count: Longint); 
    function CopyFrom(Source: TStream; Count: Longint): Longint; 
    property Position: Longint; read write; 
    property Size: Longint; read write; 
end; 

THandleStream = class(TStream) 
    constructor Create(AHandle: Integer); 
    property Handle: Integer; read; 
end; 

TFileStream = class(THandleStream) 
    constructor Create(Filename: String; Mode: Word); 
end; 

Используйте .Seek(-4, soFromEnd) свойство искать указатель чтения в нужное положение.

Усложнение состоит в том, что TStream работает с символами, а не байтами, поэтому вам нужно преобразовать строки Unicode, которые вы читаете в байтах.

При чтении всего четыре байта, это намного легче читать побайтно, предотвращая любые преобразования мультибайтные:

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


Кстати, для меня LoadStringFromFile function, кажется, effici достаточно для загрузки 50 МБ файла. Это займет всего 40 мс.

+0

Лучше использовать 'Seek' с происхождением' soEnd'. Вы будете сохранять накладные расходы на поиск начала и конца при определении размера потока. – TLama

+0

@TLama Конечно, спасибо, я упустил «Seek». Хотя я не смог использовать 'TFileStream'. И «Чтение», и «ReadBuffer» имеют неправильную подпись. «Буфер» не является аргументом «по рефлектору». –

+1

Все должно быть хорошо. Вы могли бы забыть выделить буфер. Это то, что 'ReadBuffer' не делает для вас. Хуже того, что в Unicode Inno Setup вам придется разбивать каждый символ на байты, чтобы получить символы ANSI из Unicode. Должен быть достаточно массив байтовых параметров. Постскриптум OP знает о функции LoadStringFromFile. – TLama

 Смежные вопросы

  • Нет связанных вопросов^_^