Home » Tipps & Tricks » Dateien/Verzeichnisse » Dateioperationen » Dateien patchen

Dateien patchen

Mit folgender Routine ist es möglich, in einer Datei nach einer Zeichenkette zu suchen und diese durch eine gleichlange Zeichenkette zu ersetzen. Vorsicht ist bei Dateien geboten, bei denen zwischendurch das Zeichen #0 vorkommt (beispielsweise bei EXE-Dateien). Die Routine bricht an einer solchen Stelle ab.

Die Funktion erwartet vier Parameter. Dem ersten Parameter wird der Dateiname übergeben. Die Parameter AOldText und ANewText enthalten die zu ersetzenden Strings. Der letzte Parameter gibt den Suchmodus an (siehe Dokumentation unter „StringReplace (Funktion)“).

type
TReplaceFlags = set of (rfReplaceAll, rfIgnoreCase);

function PatchFile(const AFilename: string; AOldText, ANewText: string;
         Flags: TReplaceFlags): boolean;
const
  cMaxBuffer = 128*1024;
var
  f: TFileStream;
  i: integer;
  Buf: array[0..cMaxBuffer-1] of Char;
  OldStr: string;
  StrBuf: string;
begin
  result := false;
  if Length(AOldText)   Length(ANewText) then
    exit;
  ZeroMemory(@Buf, cMaxBuffer);
  if rfIgnoreCase in Flags then
    OldStr := AnsiLowerCase(AOldText)
  else
    OldStr := AOldText;

  f := TFileStream.Create(AFilename, fmOpenReadWrite or fmShareDenyWrite);
  try

    while f.Read(Buf, cMaxBuffer) > Length(AOldText) do
    begin
      StrBuf := Buf;
      f.Seek(-Length(AOldText), soFromCurrent);
      if rfIgnoreCase in Flags then
        StrBuf := AnsiLowerCase(StrBuf);

      repeat
       i := Pos(OldStr, StrBuf);
       if i > 0 then
       begin
         result := true;
         Delete(StrBuf, i, length(AOldText));
         Insert(ANewText, StrBuf, i);
         if not (rfReplaceAll in Flags) then
           break;
       end;  {if i > 0}
     until i = 0;
     f.Position := Max(f.Position + Length(AOldText) - cMaxBuffer, 0);
     f.Write(Pointer(StrBuf)^, Length(StrBuf));
     if result and not (rfReplaceAll in Flags) then
       exit;

    end;  {while}
  finally
    f.Free;
  end;
end;

Aufgerufen werden kann die Funktion beispielsweise so:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if PatchFile('c:abc.abc', 'Test', 'Text', [rfIgnoreCase, rfReplaceAll]) then
    ShowMessage('gefunden')
  else
    ShowMessage('Nicht gefunden');
end;