Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Find and Replace in Textfile (https://www.delphipraxis.net/138945-find-replace-textfile.html)

aegidos 20. Aug 2009 10:47


Find and Replace in Textfile
 
Hy alle zusammen.

Ich habe hier im Forum einen überaus hilfreichen Beitrag zum Suchen und Ersetzen gefunden:
Hier im Forum suchenReplaceInFile
Delphi-Quellcode:
function TTestProgrammDlg.ReplaceInFile(const FileName, SearchString, NewString: AnsiString;
  CaseSensitive: Boolean): Longint;
  { returns position of string in file or -1, if not found }
const
  BufferSize = $8001; { 32K+1 bytes }
var
  pBuf, pEnd, pScan, pPos: PAnsiChar;
  filesize: LongInt;
  bytesRemaining: LongInt;
  bytesToRead: Integer;
  F: file;
  SearchFor: PAnsiChar;
  oldMode: Word;
begin
  Result := -1; { assume failure }
  // NEW (first line): if length of string to search and new string does not match, cancel.
  // Of course one could move the rest of the file accordingly instead.
  if (Length(SearchString) <> Length(NewString))
    or (Length(SearchString) = 0) or (Length(FileName) = 0) then Exit;
  SearchFor := nil;
  pBuf     := nil;

  { open file as binary, 1 byte recordsize }
  AssignFile(F, FileName);
  oldMode := FileMode;
  FileMode := fmOpenReadWrite;   { NEW: access to read and write }
  Reset(F, 1);
  FileMode := oldMode;
  try
    { allocate memory for buffer and pchar search string }
    SearchFor := StrAlloc(Length(SearchString) + 1);
    StrPCopy(SearchFor, SearchString);
    if not caseSensitive then { convert to upper case }
      AnsiUpper(SearchFor);
    GetMem(pBuf, BufferSize);
    filesize      := System.Filesize(F);
    bytesRemaining := filesize;
    pPos          := nil;
    while bytesRemaining > 0 do
    begin
      { calc how many bytes to read this round }
      if bytesRemaining >= BufferSize then
        bytesToRead := Pred(BufferSize)
      else
        bytesToRead := bytesRemaining;

      { read a buffer full and zero-terminate the buffer }
      BlockRead(F, pBuf^, bytesToRead, bytesToRead);
      pEnd := @pBuf[bytesToRead];
      pEnd^ := #0;
       { scan the buffer. Problem: buffer may contain #0 chars! So we
         treat it as a concatenation of zero-terminated strings. }
      pScan := pBuf;
      while pScan < pEnd do
      begin
        if not caseSensitive then { convert to upper case }
          AnsiUpper(pScan);
        pPos := StrPos(pScan, SearchFor); { search for substring }
        if pPos <> nil then
        begin { Found it! }
          Result := FileSize - bytesRemaining +
            Longint(pPos) - Longint(pBuf);

          // NEW: replace it
          Seek(F, Result);
          BlockWrite(F, PAnsiChar(NewString)^, Length(NewString));
          Break;
        end;
        pScan := StrEnd(pScan);
        Inc(pScan);
      end;
      if pPos <> nil then Break;
      bytesRemaining := bytesRemaining - bytesToRead;
      if bytesRemaining > 0 then
      begin
       { no luck in this buffers load. We need to handle the case of
         the search string spanning two chunks of file now. We simply
         go back a bit in the file and read from there, thus inspecting
         some characters twice
       }
        Seek(F, FilePos(F) - Length(SearchString));
        bytesRemaining := bytesRemaining + Length(SearchString);
      end;
    end; { While }
  finally
    CloseFile(F);
    if SearchFor <> nil then StrDispose(SearchFor);
    if pBuf <> nil then FreeMem(pBuf, BufferSize);
  end;
end; { ScanFile }
Diese Funktion sucht das erste Auftauchen des Searchstrings und ersetzt diesen im gegebenen File durch den NewString.
Wie muss man denn diese Funktion anpassen um jedes Vorkommen des Searchstrings zu ersetzen?

Danke für die Hilfe!
Andi

Norbert987 20. Aug 2009 10:53

Re: Find and Replace in Textfile
 
Hallo aegidos,

was spricht denn gegen StringReplace?

StringReplace(strText, alt, neu, [rfReplaceAll]);

du lädst deine Textdatei in eine TStringList und gehst dann Zeile für Zeile mit StringReplace durch. strText ist die Zeile aus der Textdatei, und alt wird dann nach neu ersetzt.

p80286 20. Aug 2009 11:01

Re: Find and Replace in Textfile
 
Hallo Andi,

ich verstehe Deine Frage nicht so ganz, da Du augenscheinlich den Source-Text verstanden hast. Du könntest z.b. das Break entfernen, den Dateizeiger um eine "Einheit" zurück setzen, und dann weiter machen.

Wo klemmt es denn konkret?

Gruß
K-H

alzaimar 20. Aug 2009 12:57

Re: Find and Replace in Textfile
 
Die o.g. Routine funktioniert nicht, wenn der neue String eine andere Länge als der alte String aufweist. Reicht Dir das?
Ich würde den Vorschlag von Norbert987 aufgreifen:
Delphi-Quellcode:
Procedure ReplaceInFile (Const aFilename, aOldString, aNewString : String);
Var
  slData : TStringList;
  i : Integer;

Begin
  slData := TStringlist.Create;
  Try
    slData.LoadFromFile (aFileName);
    For i:=0 to slData.Count - 1 do
      slData[i] := StringReplace (slData[i], aOldString, aNewString, [rfReplaceAll]);
    slData.SaveToFile(aFilename);
  Finally
    slData.Free;
  End
End;
Falls Dir Flexibilität wichtiger als Geschwindigkeit ist, kannst Du die ReplaceFlags auch parametrisieren:
Delphi-Quellcode:
Procedure ReplaceInFile (Const aFilename, aOldString, aNewString : String; aReplaceFlags : TReplaceFlags);
Var
  slData : TStringList;

Begin
  slData := TStringlist.Create;
  Try
    slData.LoadFromFile (aFileName);
    slData.Text := StringReplace (slData.Text, aOldString, aNewString, aReplaceFlags);
    slData.SaveToFile(aFilename);
  Finally
    slData.Free;
  End
End;
Diese Routine lässt sich natürlich noch optimieren. Für beide Varianten benötigst Du die Unit 'SysUtils'

aegidos 21. Aug 2009 07:30

Re: Find and Replace in Textfile
 
Hy alzaimar,
Danke für die Funktion. Geschwindigkeit ist mir an dieser Stelle doch wichtiger. Ich habe die erste Implementierung gewählt. funktioniert hervorragend , Danke !!!

@p80286:
Konkret hats daran geklemmt den Dateizeiger zurückzudrehen. Ich habe tatsächlich schon vorher versucht durch entfernen des Break die Funktion umzuschrieben. pPos habe ich dann wieder auf nil gesetzt aber ohne erfolg. Den Dateizeiger hatte ich da bei der betrachtung offensichtlich völlig vergessen.

@Norbert987:
Gute Idee, genau das ist es ja letztendlich auch geworden. Dachte anfangs einfach nur dass die Funktion die ich in dem anderen Thread gefunden habe schneller ist. Aber von Performance her nimmt sichs nicht viel. Außeßerdem war ich nur neugierig warum die Funktion die ich anfangs verwendet habe nur ein Auftauchen des Strings ersetzt und warum ich nicht fähig war sie auf ReplaceAllOccurrences umzutexten :-)

Danke an Alle !!!

Jens01 29. Sep 2009 17:20

Re: Find and Replace in Textfile
 
um ReplaceInFile für D2009 lauffähig zu bekommen, muß
SearchFor := StrAlloc(Length(SearchString) + 1); -> SearchFor := AnsiStrAlloc(Length(SearchString) + 1);
werden.
Jedenfalls bei mir.


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:53 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz