TFileStream со смещением

#delphi #stream #tfilestream #tstream

#delphi #поток #tfilestream #tstream

Вопрос:

Мне нужно расширить TFileStream, чтобы он мог работать с файлом не со смещением 0, а со смещением, определенным пользователем. Я имею в виду, что он должен интерпретировать определенное пользователем смещение как начало потока. Мой код:

 
type
  TSuFileStream = class(TFileStream)
  protected
    FOffset : int64;

    procedure SetOffset(Offset : int64);

    procedure SetSize(NewSize: Longint); override;
    procedure SetSize(const NewSize: Int64); override;
  public
    constructor Create(const AFileName: string; Mode: Word); overload;
    constructor Create(const AFileName: string; Mode: Word; Rights: Cardinal); overload;

    function Seek(Offset: Longint; Origin: Word): Longint; override;
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;

    property Offset : int64 read FOffset write SetOffset;
  end;
...
constructor TSuFileStream.Create(const AFileName: string; Mode: Word);
begin
  inherited Create(AFileName, Mode);
  FOffset := 0;
end;

constructor TSuFileStream.Create(const AFileName: string; Mode: Word; Rights: Cardinal);
begin
  inherited Create(AFileName, Mode, Rights);
  FOffset := 0;
end;

procedure TSuFileStream.SetOffset(Offset : int64);
begin
  FOffset := Offset;
  inherited Seek(FOffset, soBeginning);
end;

procedure TSuFileStream.SetSize(NewSize: Longint);
begin
  inherited SetSize(FOffset   NewSize);
end;

procedure TSuFileStream.SetSize(const NewSize: Int64);
begin
  inherited SetSize(FOffset   NewSize);
end;

function TSuFileStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
  Result := Seek(Int64(Offset), TSeekOrigin(Origin));
end;

function TSuFileStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
begin
  case Origin of
    soBeginning: Result := inherited Seek(FOffset   Offset, soBeginning) - FOffset;
    soCurrent: Result := inherited Seek(Offset, soCurrent) - FOffset;
    soEnd: Result := inherited Seek(Offset, soEnd) - FOffset;
  end;
end;
   

но это не работает должным образом. Проблема в функции поиска, но я не знаю почему. Когда я передаю такой поток стороннему компоненту, он работает, только если TSuFileStream.Смещение:= 0;

Ответ №1:

Сначала переопределите только одну из версий метода. Как вы можете видеть из интерфейса класса, у вас есть как longint, так и int64 версии одних и тех же методов (например, setSize и seek). Это в документации Delphi. Переопределите версии int64.

Во-вторых, я бы не переопределял TFileStream, а скорее TStream напрямую, чтобы создать «промежуточный поток» для работы.

В конструкторе я бы поместил 2 параметра:

  1. Фактический исходный поток любого типа
  2. Смещение

Итак, по сути, вы хотите создать прокси-сервер между реальным потоком и вашей пользовательской версией. Таким образом, в вашей реализации seek все, что вам нужно, это добавить смещение (посмотрите на TMemoryStream и TFileStream, чтобы увидеть, как это делается) к позиции. Вы также получаете преимущество поддержки любого типа источника потока.

В итоге у вас должен получиться простой в использовании прокси-сервер:

 mMyStream:=TMyProxyStream.Create(mRealStream,2800); //Root offset at 2800
try
  mMyStream.Read(mBuffer,1024); // After read, offset = 3824
  mMyStream.position:=0; //Reset offset back to to 2800
finally
  mMyStream.free;
end;
  

Функциональность поиска может быть немного сложной для вычисления. Вот пример из прокси-класса, который я закодировал для своей буферной системы (FOffset является внутренней переменной, именно с ней вы хотите манипулировать):

 function TSLBufferStreamAdapter.Seek(const Offset:Int64;
         Origin:TSeekOrigin):Int64;
Begin
  Case Origin of
  soBeginning:
    Begin
      if Offset>=0 then
      FOffset:=Math.EnsureRange(Offset,0,FBufObj.Size);
    end;
  soCurrent:
    Begin
      FOffset:=math.EnsureRange(FOffset   Offset,0,FBufObj.Size);
    end;
  soEnd:
    Begin
      If Offset>0 then
      FOffset:=FBufObj.Size-1 else
      FOffset:=math.EnsureRange(FOffset-(abs(Offset)),0,FBufObj.Size);
    end;
  end;
  result:=FOffset;
end;
  

Я обновляю этот ответ сейчас, чтобы включить ссылку на обновление. Моя библиотека byterage перенесена в Google code — посмотрите там. Надеюсь, это поможет!

Ответ №2:

Используйте TGpStreamWindow, доступный на моем веб сайте и в Google Code.

Использование:

 var
  offsetStream: TGpStreamWindow;

begin
  offsetStream := TGpStreamWindow.Create(originalStream, initialOffset, originalStream.Size - 1);
  try
    DoSomethingWith(offsetStream);
    offsetStream.SetWindow(anotherInitialOffset, originalStream.Size - 1);
    DoSomethingElseWith(offsetStream);
  finally FreeAndNil(offsetStream); end;
end;