views:

118

answers:

2

Hi!

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!

+7  A: 

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;
gabr
I guess the first `FreeAndNil` should be for `ms`.
Ulrich Gerhardt
Ulrich, thanks. Fixed.
gabr
Thanks. I think it is a most compact solution for this task.
SomeOne
+2  A: 

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.

The_Fox
better than Ulrich's, as it allows for files of unlimited size. You might want to adjust buffer size for better performance.
PA