Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delete first N characters from TextFile without creating a new file (Delphi)

I just wanna to delete from specified text file first N characters, but i'm stuck. Help me please!

procedure HeadCrop(const AFileName: string; const AHowMuch: Integer);
var
  F: TextFile;
begin
  AssignFile(F, AFileName);
  // what me to do next?
  // ...
  // if AHowMuch = 3 and file contains"Hello!" after all statements
  // it must contain "lo!"
  // ...
  CloseFile(F);
end;

I'm tried to use TStringList but its additionally appends end-line character!

with TStringList.Create do
try
  LoadFormFile(AFileName); // before - "Hello!"
  // even there are no changes...
  SaveToFile(AFileName); // after - "Hello!#13#10"
finally
  Free;
end;

Thanks!

like image 288
SomeOne Avatar asked Oct 19 '10 09:10

SomeOne


2 Answers

There's no simple way to delete something from the start of file in Windows. You have to either copy the file to another file, delete the original and rename the target file or copy all the data in the file few bytes back and then truncate the file. If the file is small and can be loaded into memory, the latter method becomes quite simple.

The following code fragment implements the latter approach with a full-size memory buffer.

var
  fs: TFileStream;
  ms: TMemoryStream;

begin
  fs := TFileStream.Create('somefile', fmOpenReadWrite); // catch errors here!
  try
    ms := TMemoryStream.Create;
    try
      ms.CopyFrom(fs, 0);
      ms.Position := 42; // head bytes to skip
      fs.Position := 0;
      fs.CopyFrom(ms, ms.Size - ms.Position);
      fs.Size := fs.Position;
    finally FreeAndNil(ms); end;
  finally FreeAndNil(fs); end;
end;
like image 189
gabr Avatar answered Sep 22 '22 14:09

gabr


Gabr beat me to it, but I took another approach:

procedure HeadCrop(const AFileName: string; const AHowMuch: Int64);
var
  lFileStream: TFileStream;
  lPos: Int64;
  lRead: Integer;
  lBuffer: array[0..511] of Byte;
begin
  lFileStream := TFileStream.Create(AFileName, fmOpenReadWrite);
  try
    if lFileStream.Size < AHowMuch then
    begin
      lFileStream.Size := 0;
      Exit;
    end;
    lPos := AHowMuch;
    repeat
      lFileStream.Position := lPos;
      lRead := lFileStream.Read(lBuffer, SizeOf(lBuffer));
      lFileStream.Position := lPos - AHowMuch;
      lFileStream.Write(lBuffer, lRead);
      Inc(lPos, SizeOf(lBuffer));
    until lRead <> SizeOf(lBuffer);
    lFileStream.Size := lFileStream.Size - AHowMuch;
  finally
    lFileStream.Free;
  end;
end;

This code reads the file and moves blocks of 512 bytes back in the file.

PS: this procedure only moves bytes, NOT characters! So this only works for one byte characters.

like image 34
The_Fox Avatar answered Sep 19 '22 14:09

The_Fox