Announcement

Collapse
No announcement yet.

Textdatei an bestimmten Positionen abteilen

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

  • Textdatei an bestimmten Positionen abteilen

    Ich habe eine riesengroße Textdatei(700MB),die ich teilen will und zwar nach folgender Regel.<P>
    Die große Textdatei sieht ungefähr so aus:<P>
    @ blablabla<BR>
    blablabla<BR>
    blablabla<BR>
    @ blablabla<BR>
    blablabla<BR>
    blablabla<BR>
    @ blablabla<BR>
    blablabla<BR>
    blablabla<BR>
    usw.<P>
    Der Text zwischen 2 @Zeichen gehört immer zusammen und darf nicht auseinandergerissen werden.<P>
    Wie könnte ich den Inhalt dieser riesengroßen Datei denn z.B. in 3 oder 4 kleinere Dateien aufteilen?<BR>
    Wer kann mir bitte helfen, es ist sehr dringend.<P>
    Die eigentliche Aufgabe besteht darin,die riesengroße Datei zu öffnen,<BR>
    solange nicht EOF den Inhalt zwischen 2 @Zeichen einzulesen,<BR>
    etwas zu bearbeiten und in eine andere Datei zu schreiben.<BR>
    Das habe ich schon versucht zu realisieren,<BR>
    aber da mache ich irgendetwas falsch,<BR>
    es kommt nach einer gewissen Zeit die Meldung "Zu wenig Arbeitsspeicher".<BR>
    Bei einer 200 MB großen Datei ging noch alles glatt.<BR>
    Ich finde einfach nicht heraus, wo mir der Arbeitsspeicher vollläuft<BR>
    und deshalb möchte ich jetzt die Datei einfach erst mal in kleinere Stücke teilen,<BR>
    wie gesagt, es eilt sehr.<BR>
    Danke im Vorraus<BR>
    Bea

  • #2
    Es wäre für uns einfacher, wenn Du uns die procedure, die nicht tut, postest.<p>
    Schöne Grüße, Mario Noac
    Schöne Grüße, Mario

    Comment


    • #3
      Ist bestimmt nicht besonders profihaft, aber ich schick es mal mit.<BR>
      <PRE>
      unit fakt_Spool2txt;

      interface

      uses
      Windows,
      SysUtils,
      Classes,
      Controls,
      Forms,
      Dialogs,
      StdCtrls,
      Buttons,
      FileCtrl,
      Psock;

      type
      TForm1 = class(TForm)
      BitBtn_start: TBitBtn;
      BitBtn_Beenden: TBitBtn;
      GroupBox1: TGroupBox;
      DriveComboBox1: TDriveComboBox;
      DirectoryListBox1: TDirectoryListBox;
      FileListBox1: TFileListBox;
      Label1: TLabel;
      GroupBox2: TGroupBox;
      DriveComboBox2: TDriveComboBox;
      DirectoryListBox2: TDirectoryListBox;
      Label2: TLabel;
      ListBox1: TListBox;
      Label3: TLabel;
      ListBox3: TListBox;
      procedure BitBtn_startClick(Sender: TObject);
      procedure FileListBox1Click(Sender: TObject);
      procedure BitBtn_BeendenClick(Sender: TObject);
      procedure DirectoryListBox2Change(Sender: TObject);
      function GetStrCount(s, ts: string): Integer;
      function InStr(p: Integer; s, ts: string): LongInt;
      private
      { Private-Deklarationen }

      public
      { Public-Deklarationen }
      end;

      var
      Form1: TForm1;
      Pfad, Datei: string;


      implementation

      {$R *.dfm}

      function TForm1.GetStrCount(s, ts: string): Longint;
      var p: Longint;

      begin
      p := 0;
      result := 0;
      while InStr(p + 1, s, ts) > 0 do
      begin
      p := InStr(p + 1, s, ts);
      result := result + 1;
      end;

      end;

      function TForm1.InStr(p: Longint; s, ts: string): LongInt;
      var ip: Integer;
      begin
      ip := 0;
      ip := Pos(ts, Copy(s, p, Length(s) - p + 1));
      if ip > 0 then
      result := p - 1 + ip
      else
      result := 0;
      end;

      function IsDate(str: string): Boolean;
      var
      dt: TDateTime;
      begin
      Result := True;
      try
      dt := StrToDate(str);
      except
      Result := False;
      end;
      end;

      function FileToString(Path: string; var ReturnStr: string): boolean;
      var
      FileStream: TFileStream;

      begin
      Result := False;
      ReturnStr := EmptyStr;
      if not FileExists(Path) then Exit;

      FileStream := TFileStream.Create(Path, fmOpenRead);
      try
      if FileStream.Size > 0 then
      begin
      SetLength(ReturnStr, FileStream.Size);
      FileStream.Read(ReturnStr[1], FileStream.Size);
      Result := True;
      end;
      finally
      //FreeAndNil(FileStream);
      FileStream.Free;
      end;
      end;

      procedure TForm1.FileListBox1Click(Sender: TObject);
      begin
      Datei := FileListBox1.FileName;
      Label1.Caption := Datei;
      end;

      procedure TForm1.BitBtn_startClick(Sender: TObject);
      var Save_Cursor: TCursor;
      FromF: file;
      ToF, F: Textfile;
      Buf: array[1..1] of Char;
      ADate: TDateTime;
      Docnr, verznr, j, b, i: Longint;
      NumRead: integer;
      c: double;
      Teilstr, Teilstr1, Inda, Inda1, newname, Inhalt, Ext: string;
      newdir, Indatmp, Indatmp1, Indatmp2: string;
      begin
      i := 1;
      verznr := 1;
      Docnr := 0;
      Save_Cursor := Screen.Cursor;
      try
      if Datei = '' then MessageDlg('Sie müssen zuerst die Datei auswählen!', mtError, [mbOK], 0)
      else
      begin
      Screen.Cursor := crHourGlass;
      AssignFile(FromF, Datei);
      Reset(FromF, 1);
      if FileExists('Indexdatei.ind') then
      begin
      Form1.Hide;
      repeat BlockRead(FromF, Buf, SizeOf(Buf), NumRead);
      if (Buf <> '@') and not (EOF(FromF)) then
      begin
      Teilstr := teilstr + Buf;
      end else
      begin
      Teilstr1 := '@' + copy(Teilstr, 1, Length(Teilstr) - 1);
      Inc(Docnr);
      Listbox3.Items.Add(Teilstr1 + '^' + #$D);
      Ext := '';
      Indatmp := '';
      Indatmp1 := '';
      j := 1;
      b := 1;

      while (j <> 0) do
      begin
      Inhalt := '';
      Inda := nthword(Listbox3.Items.GetText, '^', j);

      //nur wenn string kein Zahlenwert ist dann
      if TryStrToFloat(Inda, c) and (Inda <> ',') then
      Inda := StringReplace(I

      Comment


      • #4
        Ist bestimmt nicht besonders professionell:<P>
        <PRE>
        begin
        i := 1;
        verznr := 1;
        Docnr := 0;
        Save_Cursor := Screen.Cursor;
        try
        if Datei = '' then MessageDlg('Sie müssen zuerst die Datei auswählen!', mtError, [mbOK], 0)
        else
        begin
        Screen.Cursor := crHourGlass;
        AssignFile(FromF, Datei);
        Reset(FromF, 1);
        if FileExists('Indexdatei.ind') then
        begin
        Form1.Hide;
        repeat BlockRead(FromF, Buf, SizeOf(Buf), NumRead);
        if (Buf <> '@') and not (EOF(FromF)) then
        begin
        Teilstr := teilstr + Buf;
        end else
        begin
        Teilstr1 := '@' + copy(Teilstr, 1, Length(Teilstr) - 1);
        Inc(Docnr);
        Listbox3.Items.Add(Teilstr1 + '^' + #$D);
        Ext := '';
        Indatmp := '';
        Indatmp1 := '';
        j := 1;
        b := 1;
        while (j <> 0) do
        begin
        Inhalt := '';
        Inda := nthword(Listbox3.Items.GetText, '^', j);
        if TryStrToFloat(Inda, c) and (Inda <> ',') then
        Inda := StringReplace(Inda, ',', '.', [rfReplaceAll, rfIgnoreCase]);
        Inda := StringReplace(Inda, ',', ';', [rfReplaceAll, rfIgnoreCase]);
        if (GetStrCount(Inda, #$D#$A) > 1) and (GetStrCount(Inda, #$D#$A) < 5) then
        Inda := StringReplace(Inda, #$D#$A, ';', [rfReplaceAll, rfIgnoreCase]);
        if GetStrCount(Inda, #$D#$A) = 1 then
        begin
        Inda := ' ' + ';' + Inda;
        Inda := StringReplace(Inda, #$D#$A, ';', [rfReplaceAll, rfIgnoreCase]);
        end;
        if IsDate(Inda) then
        begin
        ADate := StrToDate(Inda);
        DateSeparator := '.';
        ShortDateFormat := 'dd/mm/yyyy';
        Inda := DateToStr(ADate);
        end;
        Application.ProcessMessages;
        if (Length(Inda) < 100) then
        begin
        if (Inda <> ';') then
        begin
        Listbox1.Items.Add(Inda);
        end;
        end else
        begin
        Inhalt := Inda;
        newname := 'DOCU' + IntToStr(Docnr);
        Ext := 'txt';
        newdir := Pfad + IntToStr(verznr) + '\';
        if ForceDirectories(newdir) then
        begin
        assignfile(F, newdir + newname + '.' + Ext);
        rewrite(F);
        writeln(F, Inhalt);
        closefile(F);
        Inc(Docnr);
        if b = 1 then
        begin
        Listbox1.Items.Strings[0] := newname;
        Inc(b);
        end;
        end;
        Application.ProcessMessages;
        end;
        Inc(j);
        if j = 3 then Inc(j);
        if (Inda = ' ;' + #$D + ';') or (Inda = #$D) then j := 0;
        end;
        Inda1 := Listbox1.Items.CommaText;
        Inda1 := StringReplace(Inda1, '@IMPHDR', newname, [rfReplaceAll, rfIgnoreCase]);
        Inda1 := StringReplace(Inda1, ',', ';', [rfReplaceAll, rfIgnoreCase]);
        Inda1 := StringReplace(Inda1, '"', '', [rfReplaceAll, rfIgnoreCase]);
        Indatmp2 := Indatmp2 + Inda1;
        Inda1 := '';
        Listbox1.Clear;
        Teilstr := '';
        ListBox3.Clear;
        Inc(verznr);
        Indatmp2 := StringReplace(Indatmp2, #$D#$A, ' ', [rfReplaceAll, rfIgnoreCase]);
        Indatmp2 := StringReplace(Indatmp2, '; ;' + #$D + ';', '', [rfReplaceAll, rfIgnoreCase]);
        Indatmp2 := StringReplace(Indatmp2, ';' + #$D, '', [rfReplaceAll, rfIgnoreCase]);
        TrimRight(Indatmp2);
        AssignFile(ToF, 'Indexdatei.ind');

        Comment


        • #5
          <PRE> i := 1;
          verznr := 1;
          Docnr := 0;
          Save_Cursor := Screen.Cursor;
          try
          if Datei = '' then MessageDlg('Sie müssen zuerst die Datei auswählen!', mtError, [mbOK], 0)
          else
          begin
          Screen.Cursor := crHourGlass;
          AssignFile(FromF, Datei);
          Reset(FromF, 1);
          if FileExists('Indexdatei.ind') then
          begin
          Form1.Hide;
          repeat BlockRead(FromF, Buf, SizeOf(Buf), NumRead);
          if (Buf <> '@') and not (EOF(FromF)) then
          begin
          Teilstr := teilstr + Buf;
          end else
          begin
          Teilstr1 := '@' + copy(Teilstr, 1, Length(Teilstr) - 1);
          Inc(Docnr);
          Listbox3.Items.Add(Teilstr1 + '^' + #$D);
          Ext := '';
          Indatmp := '';
          Indatmp1 := '';
          j := 1;
          b := 1;
          while (j <> 0) do
          begin
          Inhalt := '';
          Inda := nthword(Listbox3.Items.GetText, '^', j);
          if TryStrToFloat(Inda, c) and (Inda <> ',') then
          Inda := StringReplace(Inda, ',', '.', [rfReplaceAll, rfIgnoreCase]);
          Inda := StringReplace(Inda, ',', ';', [rfReplaceAll, rfIgnoreCase]);
          if (GetStrCount(Inda, #$D#$A) > 1) and (GetStrCount(Inda, #$D#$A) < 5) then
          Inda := StringReplace(Inda, #$D#$A, ';', [rfReplaceAll, rfIgnoreCase]);
          if GetStrCount(Inda, #$D#$A) = 1 then
          begin
          Inda := ' ' + ';' + Inda;
          Inda := StringReplace(Inda, #$D#$A, ';', [rfReplaceAll, rfIgnoreCase]);
          end;
          if IsDate(Inda) then
          begin
          ADate := StrToDate(Inda);
          DateSeparator := '.';
          ShortDateFormat := 'dd/mm/yyyy';
          Inda := DateToStr(ADate);
          end;
          Application.ProcessMessages;
          if (Length(Inda) < 100) then
          begin
          if (Inda <> ';') then
          begin
          Listbox1.Items.Add(Inda);
          end;
          end else
          begin
          Inhalt := Inda;
          newname := 'DOCU' + IntToStr(Docnr);
          Ext := 'txt';
          newdir := Pfad + IntToStr(verznr) + '\';
          if ForceDirectories(newdir) then
          begin
          assignfile(F, newdir + newname + '.' + Ext);
          rewrite(F);
          writeln(F, Inhalt);
          closefile(F);
          Inc(Docnr);
          if b = 1 then
          begin
          Listbox1.Items.Strings[0] := newname;
          Inc(b);
          end;
          end;
          Application.ProcessMessages;
          end;
          Inc(j);
          if j = 3 then Inc(j);
          if (Inda = ' ;' + #$D + ';') or (Inda = #$D) then j := 0;
          end;
          Inda1 := Listbox1.Items.CommaText;
          Indatmp2 := Indatmp2 + Inda1;
          Inda1 := '';
          Listbox1.Clear;
          Teilstr := '';
          ListBox3.Clear;
          Inc(verznr);
          AssignFile(ToF, 'Indexdatei.ind');
          append(ToF);
          writeln(ToF, Indatmp2);
          Flush(ToF);
          CloseFile(ToF);
          Teilstr := '';
          Inda := '';
          Inda1 := '';
          Indatmp2 := '';
          end;
          Application.ProcessMessages;
          Label3.Caption := IntToStr(i);
          Inc(i);
          until (EOF(FromF));
          CloseFile(FromF);
          end else
          begin
          ShowMessage('Indexdatei nicht vorhanden');
          end;
          end;
          finally
          Screen.Cursor := Save_Cursor;
          end;</PRE&gt

          Comment


          • #6
            Entschuldigt bitte ,wenn das nicht besonders professionell ist.<BR>
            Ist schon ganz schönes Gewurschtel, aber das was ich erreichen will macht das Programm mit der 200MB großen Datei ja richtig,<BR>
            aber irgendwo knallt es eben bei diesen 700MB

            Comment


            • #7
              <pre> repeat BlockRead(FromF, Buf, SizeOf(Buf), NumRead);
              if (Buf <> '@') and not (EOF(FromF)) then
              begin
              Teilstr := teilstr + Buf;
              end else
              </pre><p>
              Du liest in 1-Byte Schritten den Inhalt der Text-Datei in Teilstring, bis ein @ kommt? Falls ja, wie lang wird denn eine solche Unter-Text-Datei maximal? Kann es sein, dass einfach dieser Teil-String zu groß wird?<p>
              Wie ist denn der Aufbau der Textdatei? Ist sie in überschaubare Zeilen unterteilt und stehen vielleicht sogar die @-Zeichen immer an der gleichen Stelle? Falls ja, ist eine Lösung mit Readln und Writeln sicher effizienter bzw. übersichtlicher.<p>
              Schöne Grüße, Mario Noac
              Schöne Grüße, Mario

              Comment


              • #8
                Ich habe Ihnen mal eine Email mit einem Auszug aus der großen Datei geschickt.<BR>
                Ich dachte, daß die Variable Teilstring schon diesen Happen zwischen 2 @- Zeichen erfassen kann,<BR>
                ich mache sie ja vorm nächsten Durchlauf auch wieder leer.<BR>
                Ich lege ja ( wie im Quelltext erkennbar) während des Programmdurchlaufes Tausende von Ordnern mit einzelnen Textdateien an,<BR>
                was das Ziel des Auslesens dieser großen Datei ist.<BR>
                Diese Struktur brauche ich aber auch so für eine anschließende Weiterverarbeitung (Archivierung).<BR>
                Kann das vielleicht auch ein Grund sein, daß Windows da ein Problem hat

                Comment


                • #9
                  Mir fiel noch was anderes ein:<p>
                  <i> Listbox3.Items.Add(Teilstr1 + '^' + #$D);</i><p>
                  Soweit ich weiß, ist eine Listbox relativ begrenzt, was die Anzahl der fassbaren Zeichen angeht. Wenn sie also eine große Anzahl an Einträgen fassen soll, kann es auch sein, dass die Listbox einfach überläuft...<p>
                  Schöne Grüße, Mario Noac
                  Schöne Grüße, Mario

                  Comment


                  • #10
                    Aber die Listboxen mach ich doch immer wieder leer (Listbox.Clear)

                    Comment


                    • #11
                      Hallo!<br>
                      Kann man mal erfahren an welcher Stelle sich die nette Fehlermeldung zeigt?<br>
                      BYE BERN

                      Comment


                      • #12
                        Das kann ich nicht sagen, weil das Progrämmchen läuft ca.4 Stunden und dann kommt einfach ne Meldung "Zu wenig Arbeitsspeicher".<BR>
                        Da ist dann Schluß, ich kann das Programm schließen, weiß aber doch nicht ,<BR>
                        an welcher Stelle es da gerade war.<BR>
                        Beim Debuggen mit einer kleineren Testdatei geht ja alles glatt und es wird alles das gemacht,<BR>
                        was ich möchte und bei der großen Datei klappt das wie schon gesagt ne ganze Zeit lang auch alles prima.<BR>
                        Ich weiß eben nicht, an welcher Stelle in diesem blöden Quelltext der Arbeitsspeicher nach und nach ganz langsam immer mehr gefüllt wird,<BR>
                        ohne daß man es eben gleich merkt und sofort nachvollziehen kann.<BR>
                        Mario hat mir schon in der Art geholfen,<BR>
                        daß er mir erst mal gepostet hat, wie ich die Datei in kleinere Häppchen zerlege<BR>
                        und dann mein bisheriges Programm drüberlaufen lasse,<BR>
                        sodaß es mit der Abarbeitung eher fertig ist bevor der Arbeitsspeicher zu voll ist.<BR>
                        Dann beende ich mein Programm, starte es erneut und lasse es über das nächste Häppchen laufen.<BR>
                        Das ist natürlich auf keinen Fall der Weisheit letzter Schluß, aber so konnte ich mir erst mal weiterhelfen.<BR>
                        Ich würde schon trotzdem gern wissen, was ich im Programm falsch habe.<BR>
                        Be

                        Comment


                        • #13
                          Hallo Beate,

                          um das alles Speicherschonender zu gestalten würde ich als Erstes die gesamte Datei nach '@' durchsuchen und die Positionen in einer Liste speichern.

                          Im nächsten Schritt kannst Du dann die einzelnen Abschnitte laden und entsprechend weiterverarbeiten.

                          <pre>
                          function BufferScan(const aBuffer; const aCount: integer): integer; assembler;
                          asm
                          push edi // edi sichern
                          mov edi, aBuffer // Bufferadresse laden
                          mov al, '@' // @ in Register AL laden
                          mov ecx, aCount // Count in ECX laden

                          mov edx, ecx // count sichern
                          repne scasb // nach @ scannen
                          jnz @@End1 // @ nicht gefunden -> Sprung nach @@End1:
                          sub edx, ecx // Position naechste Zeile ermitteln
                          mov result,edx // Position an Result übergeben
                          jmp @@End
                          @@End1:
                          mov result, -1 // Ende des Buffer signalisieren
                          @@End:
                          pop edi // edi wiederherstellen
                          end;

                          function GetPositionsList: TList;
                          var
                          Buf: array[1..1024 * 16] of Char;
                          F: File;
                          il_segment: integer;
                          il_offset: integer;
                          il_NumRead: integer;
                          List: TList;
                          il_temp: integer;
                          begin
                          List := TList.Create;
                          AssignFile(F, 'd:\test.txt');
                          Reset(F,1);
                          try
                          il_segment := 0;
                          repeat
                          il_offset := 0;
                          BlockRead(F, Buf, sizeof(Buf), il_NumRead);
                          il_temp := 0;
                          while (il_offset < il_NumRead) and (il_temp >= 0) do
                          begin
                          il_temp := BufferScan(Buf[il_offset], il_NumRead - il_offset);
                          if il_temp >= 0 then
                          begin
                          il_offset := il_offset + il_temp;
                          List.Add(TObject(il_segment + il_offset));
                          end;
                          end;
                          il_segment := il_segment + sizeof(Buf);
                          until il_NumRead < SizeOf(Buf);
                          result := List;
                          finally
                          CloseFile(F);
                          end;
                          end;
                          </pre>

                          Die Liste enthält immer die Position nach dem '@'-Zeichen. Das Erstellen der Liste hat bei mir für eine 800 MB-Datei ca. 3,5 Min. gedauert. Über den Einsatz der Listboxen würde ich mir an Deiner Stelle auch noch einmal Gedanken machen.

                          Tschau

                          Torste

                          Comment


                          • #14
                            Hallo Beate,

                            noch eine kleine Anmerkung. Der erste Speicherfresser ist in Deinem Sourcecode der Teil
                            <pre>
                            ...
                            begin
                            Teilstr := teilstr + Buf;
                            end
                            ...
                            </pre>

                            Der String "teilstring" wird immer um 1 Byte erweitert. Der Speichermanager von Delphi kann mit dieser Konstellation nicht optimal umgehen. Es muß ständig neuer Speicher zugeordnet werden. Der vorher verwendete Speicher wird zwar vom Speichermanager als nicht verwendet registriert, aber gegenüber dem Betriebssystem nicht wieder freigegeben. Bei der nächsten Speicheranforderung versucht der Speichermanager die "alten" Speicherbereiche zu "recyceln" was bei dieser Konstellation jedoch scheitert, weil ja ein größerer zusammenhängender Speicherbereich benötigt wird. Dadurch wird der Speicherbedarf künstlich aufgebläht. Der Geschwindigkeit ist das alles natürlich auch nicht förderlich.

                            Das byteweise Einlesen aus der Datei ist aber wahrscheinlich die größte Bremse.

                            Bei meinem Vorschlag umgehts Du das byteweise lesen aus der Datei sowie die ständige Allozierung von Arbeitsspeicher.

                            Tschüß

                            Torsten

                            Solang

                            Comment


                            • #15
                              Um die ganz seltsamen Programmabbrüche zu lokalisieren eignet sich der DebugServer. (Ja man kann das auch selber schreiben aber der hier ist fertig und tuts)<br>
                              http://codecentral.borland.com/codecentral/ccweb.exe/listing?id=16065<br>
                              Einfach in Delphi die Unit eDebugServer einbinden und schon kann man vor und/oder nach jeder kritischen Programmzeile mit Debugserver.Write ( 'Bis hierhin gings gut!' ) eine Nachricht an den Debugserver senden. Dieser legt eine Textdatei an (default c:\debug.txt) und listet dort die Nachrichten fein säuberlich auf.<br>
                              Das Teil kann noch einiges mehr und wird von uns sehr intensiv in Tests etc. eingebunden.<br>
                              Kleiner Hinweis die Option /debug beim Aufruf der Exe nicht vergessen!!!<br>
                              BYE BERN

                              Comment

                              Working...
                              X