Announcement

Collapse
No announcement yet.

Ersetzen von Zeichen und/oder Zeichenketten in großen Textfiles

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Ersetzen von Zeichen und/oder Zeichenketten in großen Textfiles

    Hallo zusammen,
    ich habe folgende Problemstellung:

    Grundlage:
    Eine große Textdatei (bis zu 100-200MB) liegt mit Leerzeilen vor. Dazwischen gibt es an einigen Stellen #26 (EOF). Dies ist leider wirklich so.
    Wenn ich nun die Zeilen über Readln(f,s) einlese, so bricht der Vorgang leider beim ersten #26 ab.

    Beim Erzeugen kann ich die Textdatei leider nicht modifizieren.

    Zielsetzung:
    Ersetzen aller #26 durch #32 und gleichzeitig alle Leerzeilen eliminieren.

    Lösungsansatz:
    Ersetzen der Readln-Routine:
    <PRE>
    function Save_Readln2(var f:fileofchar; var s:string) : boolean;
    var c:char;
    lf:boolean;
    begin
    c:=#0;
    lf:=false;
    while not eof(f) and (c<>#10) and (c<>#13) do begin
    read(f,c);
    if (c=#26) then
    c:=#32;
    if (c<>#10) and (c<>#13) then begin
    lf:=true;
    s:=s+c
    end
    end;
    Result:=lf;
    end;
    </PRE>

    Dies funktioniert zwar so wie gewünscht, aber es dauert bei einer ca. 33MB großen Datei mit ca. 650000 Zeilen leider ca. 4 min. auf einem 1GHz-Athlon-System mit 768MB RAM.

    Gibt es eine andere und schnellere Lösung?

    Gruß
    Michael

  • #2
    <p>Hallo Michael,</p>
    <p>ich verwende f&uuml;r ein &auml;hnliches Problem (; durch , ersetzen) folgenden
    Ansatz:</p>
    <p><font face="Courier New, Courier, mono" color="#006666">RichEditTemp.Lines.LoadFromFile(Sa veDialogExport.FileName);<br>
    FOR i := 0 TO RichEditTemp.Lines.Count - 1 DO<br>
    &nbsp;BEGIN<br>
    &nbsp;&nbsp;RichEditTemp.Lines[i] := StringReplace(RichEditTemp.Lines[i],<font color="#FF0000">','</font>,<font color="#FF0000">''</font>,
    [rfReplaceAll]);<br>
    &nbsp;&nbsp;RichEditTemp.Lines[i] := StringReplace(RichEditTemp.Lines[i],<font color="#FF0000">';'</font>,<font color="#FF0000">','</font>,
    [rfReplaceAll]);<br>
    &nbsp;&nbsp;Caption := <font color="#FF0000">'Export nach Outlook (Express):
    '</font> + IntToStr(i) + <font color="#FF0000">'/'</font> + IntToStr(j);<br>
    &nbsp;&nbsp;IF (i MOD 10) = 0 THEN Application.ProcessMessages;<br>
    &nbsp;END;<br>
    RichEditTemp.Lines.SaveToFile(SaveDialogExport.Fil eName);<br>
    </font><font face="Courier New, Courier, mono"> </font> </p>
    <p>Ob das bei Dir schneller ist, wei&szlig; ich nicht, aber auf einen Versuch
    kommt es an.</p>
    <p>Sch&ouml;ne Gr&uuml;&szlig;e, Mario</p&gt
    Schöne Grüße, Mario

    Comment


    • #3
      Hallo Mario,
      <BR>
      vielen Dank für Deinen schnellen Tipp.
      <P>Deine Lösung funktioniert ganz gut mit kleineren Dateien und unkritischen Ersetzungen, ist aber für meine Problemstellung leider nicht verwendbar (Speicherbedarf, Geschwindigkeit, Stabilität).</P>
      <P>Bin inzwischen auf Blockread/Blockwrite gekommen, welches wohl am besten geeignet ist :-) / (sh. Delphi-Hilfe BlockRead, BlockWrite, SaveDialog (Beispiel))</P>
      Damit kill ich im ersten Schritt zuerst #26 (ersetzen #26 durch #32) / Quelldatei -> Tempdatei
      <BR>
      Im zweiten Schritt lösche ich die Quelldatei
      <BR>
      Im dritten Schritt übertrage ich zeilenweise zurück (lasse dabei die Leerzeilen weg) / Tempdatei -> Quelldatei
      <BR>
      <P>Gut, was bleibt ist natürlich mit dieser Methode der doppelte Platzbedarf (temporär)... aber man kann ja nicht alles haben ;-)</P>
      <BR>

      Gruß Michae

      Comment


      • #4
        Nimm doch einen TFileStream. Ich wuerde auch alle Aenderungenn im ersten Schritt machen und dann die Datei nur umbenennen

        Comment


        • #5
          Hallo Robert,
          <P>würde ich auch gerne machen, wenn ich mich bei TFileStream gut auskennen würde ;-)</P>
          <P>Hier mal die Routine, wie ich sie mir bisher überlegt habe:</P>
          <PRE>
          procedure FileCleaning (Filename:String);
          var FS : TFileStream;
          Buffer : array[0..1023] of Byte;
          Replace : Boolean;
          i, Readnum, Writenum: Integer;
          begin
          FS := TFileStream.Create(Filename, fmOpenReadWrite or fmShareDenyNone); // erzeuge FileStream
          try
          repeat
          Readnum:=FS.Read(Buffer, SizeOf(Buffer)); // lese Buffer
          Replace:=False; // keine Ersetzung
          for i := 0 to Readnum-1 do // prüfe den Buffer
          if Buffer[i]=$1A then begin // liegt #26 vor?
          Replace:=True; // Ersetzung
          Buffer[i]:=$20 // Tausche #26 gegen #32 im Buffer
          end;
          if Replace then begin // muß ersetzt werden?
          FS.Seek(-Readnum,soFromCurrent); // setze die Dateiposition auf Bufferanfang zurück
          Writenum:=FS.Write(Buffer,Readnum); // schreibe Buffer
          end
          until Readnum = 0; // bis Datei komplett gelesen wurde
          finally
          FS.Free; // FileStream freigeben
          end;
          end;
          </PRE>
          <P>Ich hoffe ich habe alles richtig verstanden ;-)</P>
          <P>Es gibt aber noch Probleme:</P>
          <P>Wie kriege ich aber die Leerzeilen raus?</P>
          <P>Kann jemand Hilfestellung geben?</P>
          <BR>
          <P>Gruß Michael</P&gt

          Comment


          • #6
            Hallöchen,

            hatte neulich auch so ein doofes Problem, das ich über TFilestram gelöst habe (wobei ich zusätzlich noch Werte ändern mußte, weshalb ich mit einer Record-Structur gearbeitet habe).
            Im großen und Ganzen finde ich Deinen Code oben okay, nur das ich das wirklich zeichenweise eingelesen hatte (mal aus dem Gedächtnis):

            c := readbuffer(wert, 1);
            if c = #26
            then c := #32;
            writebuffer(c,1);

            Wenn die Leerzeile durch #10 #13 definiert ist, dann lassen die sich analog abfangen (eben immer noch das folgezeichen abfragen). Wenn Du die Leerzeichen überlesen willst, dann brauchst Du vor das writebuffer nur noch ein if c <> #0 schreiben.

            Sorry, wenn das jetzt kein schönes Codebeispiel ist - ich habe meine Lösung grade nicht hier, aber vielleicht hilft es Dir auch so.

            Grüzzlis von
            Thoma

            Comment


            • #7
              Dein Ansatz ist falsch. Wenn du die Leerzeilen entfernen willst, dann solltest du in eine neue Datei umkopieren. Danach loeschst du die alte Datei und nennst die neue Datei um. Gelesen und geschrieben wird genauso viel wie vorher

              Comment


              • #8
                Mit dem Unterschied das das Positionieren des Dateicursor entfällt.
                Will man also nur auf einer Datei arbeiten hat man das Problem das durch die Zeichenentfernung ja Lücken entstehen. In einem solchen Fall müsste also der Dateizeiger beim Lesen anders positioniert werden als beim Schreiben. Meine Test ergaben aber das genau diese Operation das Langsamste am Dateizugriff überhaupt ist. Liegt wohl am Caching. Werden aber zwei Dateien, wie von Robert vorgeschlagen, benutzt, dann können beide Dateien absolut sequentiell arbeiten, beide nutzen einen eigenen Cache und arbeiten vollständig ohne Dateicursor Positionierungen. Im Endeffekt wird dies dadurch schneller und man hat den Vorteil das man auch Zeichen zusätzlich einfügen kann.

                Gruß Hage

                Comment


                • #9
                  Und nicht zuletzt ist es auch noch absturzsicher. Die Quelldatei wird nur gelesen ud sollte daher bei einem Programmabsturz unbeschaedigt bleiben

                  Comment


                  • #10
                    Korrekt. Es hat aber auch Nachteile. Wird benötigen doppelt so viel Speicher auf der Platte

                    Comment


                    • #11
                      <p>Hallo zusammen,</p>
                      <p>vielen Dank für Eure Antworten.</p>
                      <p>Dann lasse ich den Code so wie er ist (außer, daß ich die Buffergröße inzwischen auf 64kb erhöht habe) - hab ja auch schon ca. 80% an Zeitbedarf gespart gegenüber der allerersten Methode ;-)</p>
                      <p>Mich hatte halt nur der Satz von Robert beschäftigt:<br>
                      "Nimm doch einen TFileStream. Ich wuerde auch alle Aenderungenn im ersten Schritt machen und dann die Datei nur umbenennen."</p>
                      <p>Da war ich davon ausgegangen, daß es doch in einem Rutsch funktionieren könnte.</p>
                      <p>In dem vorliegenden Fall ist mir dann die Geschwindigkeit und Stabilität doch wichtiger als der Platzbedarf auf der Platte.</p>
                      <p>Gruß Michael</p&gt

                      Comment

                      Working...
                      X