Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi 80 MB Datei - doppelte Zeilen entfernen (https://www.delphipraxis.net/84459-80-mb-datei-doppelte-zeilen-entfernen.html)

capo 17. Jan 2007 09:29


80 MB Datei - doppelte Zeilen entfernen
 
Hallo,
ich muss bei einer 80 MB großen Datei alle doppelten un leeren Zeilen entfernen
So dauert das eine Ewigkeit:
Delphi-Quellcode:
if FileExists('logfile.txt') then
begin
  FWords := THashedStringList.Create;
  application.ProcessMessages;
  FWords.LoadFromFile(
    ExtractFilePath(Application.ExeName) + 'logfile.txt');
  lv.Items.Count := FWords.Count;
  DeleteDuplicates(Fwords);
gruss vom capo

mkinzler 17. Jan 2007 09:33

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Wie sieht das Format einer Zeile aus?

Bernhard Geyer 17. Jan 2007 09:37

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Besorg dir AQTime und such die Hotspots der verbratenen Zeit

Union 17. Jan 2007 09:45

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Hallo, einen Ansatz dazu findest Du hier

Mavarik 17. Jan 2007 10:04

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Zitat:

Zitat von capo
So dauert das eine Ewigkeit:

Das kann ich mir vorstellen...

Hast Du 160MB Platz?

Dann so:
Delphi-Quellcode:
var
  fd1,fd2 : Textfile;
  S,Last : string;
begin
  assignfile(fd1,'logfile.txt');
  assignfile(fd2,'logfile.neu');
  reset(fd1);
  rewrite(fd2);
  Last := '';
  while not(eof(fd1)) do
    begin
      readln(fd1,S);
      if (trim(S)<>'') and (S <> LAST) then
        writeln(fd2,S);
      Last := S;
    end;

  Closefile(fd2);
  Closefile(fd1);
  deletefile(fd1);
  rename(fd2,'logfile.txt');
end;
Untested...QuickandDirty.. NoErrorcheck

Schreibmal wieviel schneller es war!

Frank :coder:

Der_Unwissende 17. Jan 2007 10:31

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Hi,
wie arbeitet denn die Methode DeleteDuplicates?
Und wie meinst Du denn doppelte Zeilen? Möchtest Du hier Zeilen die zweimal hintereinander stehen entfernen? Oder möchstest Du die Datei in disjunkte Zeilen zerlegen (also eine Zeile wird nur einmal in die Liste übernommen, egal wie oft und wo sie in der Datei vorkommt)?

Gruß Der Unwissende

capo 17. Jan 2007 11:53

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Hallo
danke für eure Antworten.
Die zeilen können auch 2 - x mal vorkommen, habe meine Frage falsch formulieren.
Das hier scheint dann dafür nicht das richtige zu sein, oder.
Delphi-Quellcode:
var
  fd1,fd2 : Textfile;
  S,Last : string;
begin
  assignfile(fd1,'logfile.txt');
  assignfile(fd2,'logfile.neu');
  reset(fd1);
  rewrite(fd2);
  Last := '';
  while not(eof(fd1)) do
    begin
      readln(fd1,S);
      if (trim(S)<>'') and (S <> LAST) then
        writeln(fd2,S);
      Last := S;
    end;

  Closefile(fd2);
  Closefile(fd1);
  deletefile(fd1);
  rename(fd2,'logfile.txt');
end;
hier habe ich dann auch noch einen fehler:
Zitat:

[Fehler] Unit1.pas(47): Inkompatible Typen: 'String' und 'Text'
Delphi-Quellcode:
deletefile(fd1);

mkinzler 17. Jan 2007 12:01

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Zitat:

Delphi-Quellcode:
deletefile(fd1);

DeleteFile möchte den Dateinamen:
Delphi-Quellcode:
deletefile('logfile.txt');

Mavarik 17. Jan 2007 12:01

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Sorry doch, dass ist genau das richtige...

Muss nur
Delphi-Quellcode:
delete(fd1);
heißen..

Frank :coder:

capo 17. Jan 2007 12:34

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Hallo
so mach ich das nun aber es sind immer noch doppelte Einträge vorhanden.
Also dachte ich mir lass ich das ganze nocheinmal mit der neuen Datei durchlaufen, klappt aber dennoch nicht:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  fd1,fd2 : Textfile;
  S,Last : string;
begin
  assignfile(fd1,'C:\logfile.txt');
  assignfile(fd2,'C:\logfile.neu');
  reset(fd1);
  rewrite(fd2);
  Last := '';
  while not(eof(fd1)) do
    begin
      readln(fd1,S);
      if (trim(S)<>'') and (S <> LAST) then
        writeln(fd2,S);
      Last := S;
    end;

  Closefile(fd2);
  Closefile(fd1);
  deletefile('logfile.txt');
rename(fd1,'C:\logfile.txt');
end;
gruss capo

Der_Unwissende 17. Jan 2007 12:49

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Zitat:

Zitat von capo
Delphi-Quellcode:
  while not(eof(fd1)) do
    begin
      readln(fd1,S);
      if (trim(S)<>'') and (S <> LAST) then
        writeln(fd2,S);
      Last := S;
    end;

Hier solltest Du schonmal unterscheiden, ob S leer war oder nicht (sonst suchst Du auch nur nach einer Leerzeile nur noch nach einer weiteren Leerzeile).

Delphi-Quellcode:
  while not(eof(fd1)) do
    begin
      readln(fd1,S);
      if (trim(S)<>'') and (S <> LAST) then
      begin
        writeln(fd2,S);
        Last := S;
      end;
    end;
So, damit hast Du dann die Variante, die alle aufeinander folgenden, gleichen Zeilen löscht.

Möchtest Du allgemeiner jede Zeile nur einmal haben, dann versuche es lieber mit:
Delphi-Quellcode:
var listAlt, listNeu: TStringList;
    i: Integer;
begin
  listAlt := TStringList.Create;
  // laden wie gehabt
  listAlt.LoadFromFile('...');
 
  listNeu := THashedStringList.Create;
  for i := 0 to listAlt.Count - 1 do
  begin
    if listNeu.indexOf(listAlt[i]) < 0 then
    begin
      listNeu.add(listAlt[i]);
    end;
  end;

  listNeu.SaveToFile('...');
end;
Wie Du hier siehst, wird der Hash nur für die neue Liste verwendet, die alte sollte möglichst keinen haben, da Du hier eh alle Zeilen betrachten wirst. Das Berechnen eines Hashs bringt dir also keinen Vorteil, kostet aber Zeit. Wird nun nach dem Index eines Strings gesucht, so kann dies mit einem Hash sehr viel schneller durchgeführt werden als ohne, für die neue Liste lohnt sich also dieser Hash. Alternativ gibt es noch mind. zwei Möglichkeiten, Du kannst die HashedStringList auch durch eine Map (Alzaimar hat mal eine für Strings als Schlüssel zur Verfügung gestellt) ersetzen (die ist vielleicht noch schneller). Oder aber du setzt bei der neuen StringList die Eigenschaft sorted auf True und die Eigenschaft Duplicates auf dupIgnore. Was davon die höchste Perfomance erzielt kann ich so nicht sagen, musst Du einfach mal testen.

Gruß Der Unwissende

Mavarik 17. Jan 2007 13:11

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Zitat:

Zitat von Der_Unwissende
Delphi-Quellcode:
  while not(eof(fd1)) do
    begin
      readln(fd1,S);
      if (trim(S)<>'') and (S <> LAST) then
      begin
        writeln(fd2,S);
        Last := S;
      end;
    end;

Ja genau, das Begin end hat gefehlt...

Wer will schon aus einen Logfile alle doppelten killen? Oder ist das so..

Frank

Mavarik 17. Jan 2007 13:14

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Zitat:

Zitat von capo
Hallo
so mach ich das nun aber es sind immer noch doppelte Einträge vorhanden.
gruss capo

Die werden dann nicht 100%ig gleich sein...

ggf. mal

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  fd1,fd2 : Textfile;
  S,Last : string;
begin
  assignfile(fd1,'C:\logfile.txt');
  assignfile(fd2,'C:\logfile.neu');
  reset(fd1);
  rewrite(fd2);
  Last := '';
  while not(eof(fd1)) do
    begin
      readln(fd1,S);
      if (trim(S)<>'') and (uppercase(S) <> LAST) then
        begin
          writeln(fd2,S);
          Last := uppercase(S);
        end;
    end;

  Closefile(fd2);
  Closefile(fd1);
  delete(fd1);
  rename(fd1,'C:\logfile.txt');
end;
Frank

Der_Unwissende 17. Jan 2007 13:23

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Zitat:

Zitat von Mavarik
Wer will schon aus einen Logfile alle doppelten killen? Oder ist das so..

Hm, wo steht denn das es um ein Log-File geht? Also mir kommt es so vor, als ob die Fragen um was für eine Datei es sich handelt (irgendwo am Anfang wurde nach dem Format der Zeilen) und ob jetzt wirklich alle doppelten oder nur aufeinander folgende Doppelte entfernt werden sollen nicht weiter eingegangen wurde.

Aber selbst im Log-File, wenn Du nur sehen möchtest, was für Probleme aufgetreten sind... Also es gibt bestimmt Dateien, wo man sich einfach mal die disjunkten Zerlegungen anschauen möchte. Aber mal die Frage andersrum, welches Logfile speichert denn mehrfach aufeinander folgend exact die gleiche Zeile? (schon wenn Datum und Uhrzeit im Log vorkommen hat man hier ja schnell zwei unterschiedliche Strings).

Mavarik 17. Jan 2007 13:29

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Na ich würde sagen:
Delphi-Quellcode:
if FileExists('logfile.txt') then
Sieht mir sehr nach einem Logfile aus...

Frank

Der_Unwissende 17. Jan 2007 13:40

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Zitat:

Zitat von Mavarik
Na ich würde sagen:
Delphi-Quellcode:
if FileExists('logfile.txt') then
Sieht mir sehr nach einem Logfile aus...

Das ist doch reine Spekulation! :mrgreen:

Ok, ich fang an und lerne Lesen! Ihr habt's ja so gewollt!

capo 17. Jan 2007 17:00

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Logfile.txt heisst die Datei nur weil ich den Code aus einem anderen Programm von mir übernommen habe.
Es sind Adressen um die es geht.
gruss capo

capo 17. Jan 2007 18:42

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Delphi-Quellcode:
var listAlt, listNeu: TStringList;
    i: Integer;
begin
  listAlt := TStringList.Create;
  // laden wie gehabt
  listAlt.LoadFromFile('...');

  listNeu := THashedStringList.Create;
  for i := 0 to listAlt.Count - 1 do
  begin
    if listNeu.indexOf(listAlt[i]) < 0 then
    begin
      listNeu.add(listAlt[i]);
    end;
  end;

  listNeu.SaveToFile('...');
end;
damit läuft es nun schon eine stunde, nichts passiert

Christian Seehase 17. Jan 2007 18:52

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Moin Capo,

bist Du sicher, dass das Programm überhaupt schon mit dem Einlesen der Datei fertig ist?
80MB in eine Stringlist einzulesen kann ziemlich dauern.

Wenn Du wirklich nur doppelte Zeilen finden willst, halte ich es für sinnvoller die Datei zeilenweise zu lesen (ReadLn usw.), jeweils einen MD5-Hash über der Zeile zu bilden, diesen in einer STringList suchen. Ist er nicht vorhanden, die Zeile in eine Zieldatei wegschreiben, und den Hashwert mit in die Tabelle aufnehmen.

Die Eigenschaft Capacity (Anzahl zu erwartender Zeilen) einer StringList solltest Du bei solchen Aktionen auf einen möglichst korrekten Wert einstellen, um Geschwindigkeit herauszuholen.

MrKnogge 17. Jan 2007 19:00

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Moin Christian,

wäre es nicht schneller, die Strings direkt zu vergleichen, statt erst den "Umweg" per MD5 zu gehen ?

Gruß

Nils_13 17. Jan 2007 19:30

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Zitat:

Zitat von MrKnogge
Moin Christian,

wäre es nicht schneller, die Strings direkt zu vergleichen, statt erst den "Umweg" per MD5 zu gehen ?

Gruß

Ich bin zwar nicht Christian, aber soweit ich weiß ist direkt vergleichen schneller.

capo 17. Jan 2007 19:33

Re: 80 MB Datei - doppelte Zeilen entfernen
 
hallo und danke für eure antworten.
habs nun so gelöst:
dauert damit ca. 10 minuten
merkwürdigerweise werden dabei auch einige ; in der datei gelöscht.
insofern ist diese lösung doch nicht so geeignet.

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  SL: TStringList;
  I: integer;
begin
  SL := TStringList.Create;
  try
    SL.BeginUpdate;
    try
      SL.LoadFromFile('C:\file.txt');
      SL.Sort;
      for i := SL.Count - 2 downto 0 do
        if SL[i] = SL[i + 1] then
          SL.Delete(i + 1);
      SL.SaveToFile('C:\file_ok.txt');
    finally
      SL.EndUpdate;
    end;
  finally
    SL.Free;
  end;
  Showmessage('Fertig...');
end;
hallo Christian
hast du evt. ein beispiel, ein paar zeilen?


gruss capo

Christian Seehase 17. Jan 2007 21:10

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Moin capo,

so würde ich das wohl bei einer solchen Dateigrösse machen:

Delphi-Quellcode:
var
  fIN  : TextFile;
  fOUT : TextFile;
  sl   : TStringList;
  sLine : string;
  sMD5  : string;

begin
  sl := TStringList.Create;
  try
    sl.Sorted := true;
    AssignFile(fIN,'<Pfad der Quelldatei>');
    AssignFile(fOUT,'<Pfad der Zieldatei>');
    Reset(fIN);
    Rewrite(fOUT);
    try
      Readln(fIn,sLine);
      while not Eof(fIN) do begin
        sMD5 := MD5Print(MD5String(sLine));
        if sl.IndexOf(sMD5) = -1 then begin
          sl.Add(sMD5);
          Writeln(fOUT);
        end;
        Readln(fIN,sLine);
      end;
    finally
      CloseFile(fOUT);
      CloseFile(fIN);
    end;
  finally
    FreeAndNil(sl);
  end;
end;
für die MD5-Funktionen kannst Du dies nehmen.

Sicher kostest es Zeit die MD5 zu berechnen, um einen doppelten String zu ermitteln, aber bei der Datenmenge könnte es schneller sein. Eine andere Variante:

Delphi-Quellcode:
var
  fIN  : TextFile;
  slOut : TStringList;
  sLine : string;

begin
  slOut := TStringList.Create;
  try
    slOut.Capacity     := 1000000;
    slOut.Sorted       := True;
    slOut.CaseSensitive := False;
    slOut.Duplicates   := dupIgnore;
    AssignFile(fIN,'<Pfad der Quelldatei>');
    Reset(fIN);
    try
      Readln(fIn,sLine);
      while not Eof(fIN) do begin
        slOut.Add(sLine);
        Readln(fIN,sLine);
      end;
    finally
      CloseFile(fIN);
    end;
    slOut.SaveToFile('<Pfad der Zieldatei>');
  finally
    FreeAndNil(slOut);
  end;
end;

capo 17. Jan 2007 21:18

Re: 80 MB Datei - doppelte Zeilen entfernen
 
hallo christian
dankeschön für deine beispiele.
:-D

Hawkeye219 17. Jan 2007 22:21

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Moin Christian,

du ignorierst in beiden Varianten die letzte Zeile der Eingabedatei. Das Ergebnis der ersten Variante ist sicher auch nicht das gewünschte.

Korrektur der Variante 1:
Delphi-Quellcode:
[...]
try
//  Readln(fIn,sLine); // entfernt
  while not Eof(fIN) do begin
    Readln(fIn,sLine); // neu
    sMD5 := MD5Print(MD5String(sLine));
    if sl.IndexOf(sMD5) = -1 then begin
      sl.Add(sMD5);
      Writeln(fOUT, sLine); // korrigiert
    end;
//    Readln(fIN,sLine); // entfernt
  end;
finally
[...]
Korrektur der Variante 2:
Delphi-Quellcode:
[...]
try
//  Readln(fIn,sLine); // entfernt
  while not Eof(fIN) do begin
    Readln(fIn,sLine); // neu
    slOut.Add(sLine);
//    Readln(fIN,sLine); // entfernt
  end;
finally
[...]
Gruß Hawkeye

Mavarik 18. Jan 2007 07:29

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Zitat:

Zitat von capo
Logfile.txt heisst die Datei nur weil ich den Code aus einem anderen Programm von mir übernommen habe.
Es sind Adressen um die es geht.
gruss capo

Oh mann :stupid:

Du läßt uns hier rumrätzeln, weil Du Deine Frage so schwammig gestellt hast...

Na was den nu?

Sind die doppelten jetzt über die Datei verteilt oder liegen die hintereinander?

Wenn es 80MB sind und es sich dabei um Adressen handelt sind das bei typischer Adressgröße ca. 500.000-700.000 Adressen...(Zeilen), richtig?

Dann vielleicht sowas...

Delphi-Quellcode:
type
   TSorter = Record
              CRC  : longint;
              Zeile : longint;
            end;
var
   Sort : array of TSorter;
   Count : integer;
   S    : String;
   i    : integer;
begin
  assignfile(fd1,'Adressen.txt');
  reset(fd1);
  Count := 0;
  Setlength(Sort,500000);
  while not(eof(fd1)) do
    begin
      readln(fd1,S);
      if length(Sort) > Count+1 
        then begin
               Sort.CRC  := CRC4(S); // Oder auch den HD5
               Sort.Zeile := Count;
     
             end
        else begin
               Setlength(Sort,Count + 10000); // Je größer die Zahl, desto schneller...
             end;

      inc(Count)
    end;
  Closefile(fd1);

  QSort(Sort,0,Count); // Gibt es von mir hier eine Kombo...

  // Den rest überlasse ich Dir...
  // Doppelte aus dem Array löschen...
  // Dann nur die Zeilen in die andere Dateikopieren, wenn Count in sort.Zeile
   
end;
Frank :coder:

PS.: Und wenn es dann noch nicht schnell genug ist... Zip die Adress-Datei zusammen und leg sie irgendwo hin...
Denn das Antworten hier dauert jetzt schon länger als eine Konvertierung.. :-)

smudo 18. Jan 2007 07:57

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Ich weiß jetzt nicht, was Franks QSort so macht, aber wenn die Reihenfolge der Elemente in der Liste geändert werden darf, würde ich auf jeden Fall Shellsort vorschlagen. Das sollte bei der Datenmenge das schnellste Verfahren sein.

Baue doch einfach mal ein paar Cardinals ein und ermittle den TickCount. Dann weißt du, an welcher Stelle du am Besten optimieren solltest.

Edit: Noch was. Was machst du denn mit den Adressen? Falls du vorhast, sie anschließend in eine DB einzulesen, dann spar dir doch das Bearbeiten der Datei und mach die Logik lieber mit der DB.

Mavarik 18. Jan 2007 08:23

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Zitat:

Zitat von smudo
Ich weiß jetzt nicht, was Franks QSort so macht

Ist ein objektorientierte Quicksort Komponente ist i.d.R. die schnelleste Möglichkeit... (Bitte keine Diskussion hierrüber anfangen ) :lol:


Zitat:

Zitat von smudo
Edit: Noch was. Was machst du denn mit den Adressen? Falls du vorhast, sie anschließend in eine DB einzulesen, dann spar dir doch das Bearbeiten der Datei und mach die Logik lieber mit der DB.

Genau, das wäre mein nächster Vorschlag gewesen...

Frank :coder:

capo 18. Jan 2007 08:45

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Hallo und auch gleich ein SORRY.
Ja es sind Adressen.
Ich dachte es ist egal was in den Zeilen steht wenn ich sage das diese doppelt oder mehrfach vorkommen.
Wieder etwas gelernt.
Danke an ALLE
Gruss Capo

Mavarik 18. Jan 2007 08:54

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Zitat:

Zitat von capo
Hallo und auch gleich ein SORRY.
Gruss Capo

Ja sind Deine Fragen damit beantwortet?

Frank

capo 18. Jan 2007 09:04

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Hallo Mavarik
Danke für deine Nachfrage.
Kann's nicht kompilieren und starten
Bleibt hier hängen:
Delphi-Quellcode:
  Sort.CRC := CRC4(S); // Oder auch den HD5
Zitat:

Erzeugen
[Fehler] Unit1.pas(47): Record, Objekt oder Klassentyp erforderlich
[Fehler] Unit1.pas(48): Record, Objekt oder Klassentyp erforderlich
[Fehler] Unit1.pas(59): Undefinierter Bezeichner: 'QSort'
[Fataler Fehler] Project1.dpr(5): Verwendete Unit 'Unit1.pas' kann nicht compiliert werden
Gruss Capo

Mavarik 18. Jan 2007 09:12

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Ja klar...

Musst du halt eine CRC Funktion oder Deine HD5 Funktion einbauen...

Frank

capo 18. Jan 2007 09:39

Re: 80 MB Datei - doppelte Zeilen entfernen
 
Klar :-D


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:37 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