Можно ли удалить байты из начала файла? - PullRequest
9 голосов
/ 07 марта 2012

Я знаю, что могу эффективно обрезать файл и удалить байты из конца файла.

Существует ли соответствующий эффективный способ обрезания файлов путем удаления содержимого от начала файла до точки в середине файла?

Ответы [ 2 ]

12 голосов
/ 07 марта 2012

Когда я читаю вопрос, вы просите удалить содержимое из файла, начиная с начала файла.Другими словами, вы хотите удалить содержимое в начале файла и сдвинуть оставшееся содержимое вниз.

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

Однако, если вы сделаете это, быстрого способа сделать это не существует.Вы должны скопировать данные, например, как описывает @kobik.

Раймонд Чен написал хорошую статью на эту тему: Как мне удалить байты из начала файла?


Просто для удовольствия, вот простая реализация потокового метода для удаления контента из любой точки файла.Вы можете использовать это с потоком файлов для чтения / записи.Я не проверял код, я оставлю это вам!

procedure DeleteFromStream(Stream: TStream; Start, Length: Int64);
var
  Buffer: Pointer;
  BufferSize: Integer;
  BytesToRead: Int64;
  BytesRemaining: Int64;
  SourcePos, DestPos: Int64;
begin
  SourcePos := Start+Length;
  DestPos := Start;
  BytesRemaining := Stream.Size-SourcePos;
  BufferSize := Min(BytesRemaining, 1024*1024*16);//no bigger than 16MB
  GetMem(Buffer, BufferSize);
  try
    while BytesRemaining>0 do begin
      BytesToRead := Min(BufferSize, BytesRemaining);
      Stream.Position := SourcePos;
      Stream.ReadBuffer(Buffer^, BytesToRead);
      Stream.Position := DestPos;
      Stream.WriteBuffer(Buffer^, BytesToRead);
      inc(SourcePos, BytesToRead);
      inc(DestPos, BytesToRead);
      dec(BytesRemaining, BytesToRead);
    end;
    Stream.Size := DestPos;
  finally
    FreeMem(Buffer);
  end;
end;
7 голосов
/ 07 марта 2012

Очень простым решением было бы сместить (переместить) блоки данных из " смещения целевой позиции " в сторону BOF, а затем обрезать (обрезать) остатки:

--------------------------
|******|xxxxxx|yyyyyy|zzz|
--------------------------
BOF  <-^ (target position offset)


--------------------------
|xxxxxx|yyyyyy|zzz|******|
--------------------------
                  ^ EOF

Поскольку @David опубликовал код, основанный на TStream, здесь приведен код, основанный на стиле «низкий уровень» ввода / вывода Паскаль:

function FileDeleteFromBOF(const FileName: string; const Offset: Cardinal): Boolean;
var
  Buf: Pointer;
  BufSize, FSize,
  NumRead, NumWrite,
  OffsetFrom, OffsetTo: Cardinal;
  F: file;
begin
  {$IOCHECKS OFF}
  Result := False;
  AssignFile(F, FileName);
  try
    FileMode := 2; // Read/Write
    Reset(F, 1); // Record size = 1
    FSize := FileSize(F);
    if (IOResult <> 0) or (Offset >= FSize) then Exit;
    BufSize := Min(Offset, 1024 * 64); // Max 64k - This value could be optimized
    GetMem(Buf, BufSize);
    try
      OffsetFrom := Offset;
      OffsetTo := 0;
      repeat
        Seek(F, OffsetFrom);
        BlockRead(F, Buf^, BufSize, NumRead);
        if NumRead = 0 then Break;
        Seek(F, OffsetTo);
        BlockWrite(F, Buf^, NumRead, NumWrite);
        Inc(OffsetFrom, NumWrite);
        Inc(OffsetTo, NumWrite);
      until (NumRead = 0) or (NumWrite <> NumRead) or (OffsetFrom >= FSize);
      // Truncate and set to EOF
      Seek(F, FSize - Offset);
      Truncate(F);
      Result := IOResult = 0;
    finally
      FreeMem(Buf);
    end;
  finally
    CloseFile(F);
  end;
end;
...