AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Große Textdatei - einzelne Zeile löschen

Offene Frage von "mytbo"
Ein Thema von GummiKuh68 · begonnen am 10. Aug 2022 · letzter Beitrag vom 16. Aug 2022
Antwort Antwort
Seite 2 von 3     12 3      
Delphi.Narium

Registriert seit: 27. Nov 2017
2.508 Beiträge
 
Delphi 7 Professional
 
#11

AW: Große Textdatei - einzelne Zeile löschen

  Alt 11. Aug 2022, 11:16
Du benötigst für jeden vorkommenden Anfangsbuchstaben eine Datei.
Je Zeile prüfst Du, ob es diese Datei gibt, öffnest oder erstellst sie, schreibst hinein und schließt sie.

Welche Anfangsbuchstaben vorkommen, lässt sich sicherlich ermitteln.
Im Zweifel ist das alles von ASCII 32 bis 255. Vermutlich aber eher ASCII 49 bis 57 (für die Ziffern 0 bis 9), ASCII 65 bis 90 (für A bis Z) und ASCII 97 bis 122 (für a bis z). Eventuell noch sowas wie -_.

Erstelle Dir einmalig diese Dateien, dann kannst Du schonmal je Zeile das
Delphi-Quellcode:
  if FileExists(OFile) then
    begin
      assignFile (OutFile, OFile);
      append(OutFile);
    end
  else
einsparen.

Bei 97 GB (104.152.956.928 Byte) mit einer (geratenen) durchschnittlichen Zeilenlänge von 256 Byte, entfallen damit ca. 406.847.488 Prüfungen, ob eine Datei existiert oder nicht. Bei einer Dauer dieser Prüfung von jeweils 0,01 Sekunden, ergäbe der Wegfall dieser Prüfungen eine Laufzeitverringerung von ca. 47 Tagen.
Bei 64 KB pro Zeile wäre das noch 'ne Ersparnis von etwa 4,5 Tagen.

Wenn Du Dir nun die Dateien am Programmanfang erstellst bzw. öffnest, sie während der Programmlaufzeit offen hälst und erst zum Programmende schließt, sparst Du auch noch die ReWrites und CloseFiles je Zeile. Könnte dann auch die Laufzeit spürbar verkürzen.

Eventuell könnte ja sowas in der Art funktionieren (ungetestet hingedaddelt):
Delphi-Quellcode:
type
  rFile = record
    AFile : TextFile;
    AFileName : String;
  end;
  TFiles = Array[32..255] of rFile;

var
  Files : TFiles;

procedure TForm1.cb3Click(Sender: TObject);
var
  i : Integer;
  sZeile : String;
  inFile : TextFile;
begin
  for i := Low(Files) to High(Files) do begin
    Files[i].AFileName := Format('i:\rockyou2021-%.3d.txt',[i]);
    AssignFile(Files[i].AFile,Files[i].AFileName);
    case FileExists(Files[i].AFileName) of
      true : Append(Files[i].AFile);
      false : ReWrite(Files[i].AFile);
    end;
  end;
  AssignFile(Infile,'I:\rockyou2022.txt');
  Reset(Infile);
  while not EoF(InFile) do begin
    ReadLn(InFile, sZeile);
    if sZeile <> 'then WriteLn(Files[Ord(sZeile[1])].AFile,sZeile);
  end;
  CloseFile(InFile);
  for i := Low(Files) to High(Files) do CloseFile(Files[i].AFile);
end;

Geändert von Delphi.Narium (11. Aug 2022 um 11:17 Uhr) Grund: Schreibfehler
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#12

AW: Große Textdatei - einzelne Zeile löschen

  Alt 11. Aug 2022, 11:44
Wenn man die Dateien/Streams selber öffnet (CreateFile), dann kann man auch das Windows-Caching positiv beeinflussen -> Windows sagen, dass man Sequentiell ließt/schreibt und nicht ramdom (kreuz und quer).
Dann statt AssignFile, mit seinem zusätzlichem extrem unoptimalen Caching, gäbe es bessere Stream-Funktionen,
aber zumindestens könnte man beim Reset/Rewirte (nicht Append) die RecSize etwas anpassen, damit der Cache im Delphi etwas flotter wird.


[edit] opps, 97 GB ... nicht MB [/edit]


egal: mit Win64 kompiliert und ausreichend Auslagerungsdatei (und natürlich freie Festplatte dafür)
oder ein kleiner 128GB oder 256GB RAM-Riegel
https://docs.microsoft.com/de-de/win...ndows-releases

* eine TStringList zum Laden
* danach ist jede Zeile als einzelner String im Speicher

* dann ein Array/Liste mit TStringList für jeden Anfangsbuchstaben
* * alle Ausgabedateien zu Beginn erstellen, oder erst wenn benötogt
* * beim Array kann man auf nil prüfen (mit SetLength vorher einfach auf High(Char)+1 setzen)
* * bei der TList, oder besser einem TDictionary (tja, selbst schuld, wenn man altes Delphi nutzt), kann man z.B. mit Exists oder TryGet prüfen

* wenn die Ausgabedateien bereits existieren und nicht vollständig "neu" überschrieben werden
dann deren Inhalt vorher in die jeweilige TStirngList laden

* beim "Umkopieren" der Strings in die neue Datei (TStringList) wir kein weiterer Arbeitsspeicher benötigt (da Strings mit Referenzzählung)

* tja, und am Ende dann alle Ausgabedateien "einmal" speichern, wenn vorhanden ( <>nil ), bzw. wenn Count=0



PS: Man kann auch die Eingangsdatei (TStringList) nach dem Laden einmal sortieren,
dann lässt sich anschließend jede Ausgabedatei "nacheinander" da rausholen und speichern (ein Buchstabe nach dem Anderen, wobei jeweils nur eine Ausgabedatei gleichzeitig nötig ist),
da bereits alle jeweiligen Anfangsbuchstaben zusammenhängend nacheinander in der Liste liegen.



Und wie schon beim Vorredner genannt:
Sequentiell gelesen und gespeichert (Streams oder die uralten Pascal-Dateifunktionen statt TStringList), aber alle Ausgabedateien offen lassen ... etwa gleich schnell und aufwändig, aber mit ein bissl weniger Arbeitsspeicherverbrauch.
$2B or not $2B

Geändert von himitsu (11. Aug 2022 um 12:00 Uhr)
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.508 Beiträge
 
Delphi 7 Professional
 
#13

AW: Große Textdatei - einzelne Zeile löschen

  Alt 11. Aug 2022, 15:23
Mal so JustForFun eine Variante mit FileStream und den alten Dateifunktionen aus Pascalzeiten.
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, IniFiles;

type
  rFile = record
    AFile : TextFile;
    AFileName : String;
  end;

  TFiles = Array[32..255] of rFile;

type
  TForm1 = class(TForm)
    btnOpen: TButton;
    btnDoIt: TButton;
    btnCancel: TButton;
    btnClose: TButton;
    btnEnde: TButton;
    stb: TStatusBar;
    procedure FormCreate(Sender: TObject);
    procedure btnOpenClick(Sender: TObject);
    procedure btnDoItClick(Sender: TObject);
    procedure btnCancelClick(Sender: TObject);
    procedure btnCloseClick(Sender: TObject);
    procedure btnEndeClick(Sender: TObject);
  private
    { Private-Deklarationen }
    fInputFile : String;
    fOutputFiles : String;
    fPosition : Int64;
    fFiles : TFiles;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

// Das sollte man sinnvollerweise in eine INI-Datei schreiben.
const
  csInputFile = 'I:\rockyou2021.txt';
  csOutputFiles = 'I:\rockyou2021-%.3d.txt';
  ciPosition = 0;

procedure TForm1.FormCreate(Sender: TObject);
var
  Ini : TIniFile;
begin
  Ini := TIniFile.Create(ChangeFileExt(Application.ExeName,'.ini'));
  fInputFile := Ini.ReadString('Config','InputFile',csInputFile);
  fOutputFiles := Ini.ReadString('Config','OutputFiles',csOutputFiles);
  fPosition := Ini.ReadInteger('Config','Position',ciPosition);
  Ini.Free;
end;

procedure TForm1.btnOpenClick(Sender: TObject);
var
  i : Integer;
begin
  Screen.Cursor := crHourGlass;
  if fInputFile = 'then fInputFile := csInputFile;
  if fOutputFiles = 'then fOutputFiles := csOutputFiles;
  if fPosition < 0 then fPosition := ciPosition;
  for i := Low(fFiles) to High(fFiles) do begin
    fFiles[i].AFileName := Format(fOutputFiles,[i]);
    AssignFile(fFiles[i].AFile,fFiles[i].AFileName);
    case FileExists(fFiles[i].AFileName) of
      true : Append(fFiles[i].AFile);
      false : ReWrite(fFiles[i].AFile);
    end;
  end;
  btnDoIt.SetFocus;
  Screen.Cursor := crDefault;
end;

procedure TForm1.btnDoItClick(Sender: TObject);
var
  input : TFileStream;
  ch : Char;
  sZeile : String;
  iZeilen : Integer;
  dtStart : TDateTime;
  dtEnde : TDateTime;
  dtLaufzeit : TDateTime;
  dProzent : Double;
begin
  Screen.Cursor := crHourGlass;
  btnCancel.SetFocus;
  btnCancel.Tag := 0;
  dtStart := Now;
  sZeile := '';
  iZeilen := 0;
  input := TFileStream.Create(fInputFile,fmOpenRead);
  input.Seek(fPosition, soFromBeginning);
  if input.Read(ch, 1) > 0 then begin
    repeat
      case ch of
        #10 : begin
                if sZeile <> 'then WriteLn(fFiles[Ord(sZeile[1])].AFile,sZeile);
                sZeile := '';
                iZeilen := iZeilen + 1;
                if iZeilen mod 10000 = 0 then begin
                  dProzent := input.Position * 100 / input.Size;
                  dtLaufzeit := Now - dtStart;
                  dtEnde := dtStart + (dtLaufzeit * 100 / dProzent);
                  stb.SimpleText := Format('Zeilen: %d - Position: %d von %d (%.2f%% - Ende ca.: %s)',
                                    [iZeilen,input.Position,input.Size,dProzent,DateTimeToStr(dtEnde)]);
                  Application.ProcessMessages;
                  if btnCancel.Tag <> 0 then break;
                end;
              end;
        #13 : begin end;
      else
        sZeile := sZeile + ch;
      end;
    until (input.Read(ch, 1) = 0);
  end;
  fPosition := input.Position;
  dtEnde := Now - dtStart;
  Screen.Cursor := crDefault;
  ShowMessage(Format('Zeilen: %d%sLaufzeit: %s%sPosition: %d',
              [iZeilen,sLineBreak,TimeToStr(dtEnde),sLineBreak,input.Position]));
  input.Free;
  btnClose.SetFocus;
end;

procedure TForm1.btnCancelClick(Sender: TObject);
begin
  btnCancel.Tag := 1;
end;

procedure TForm1.btnCloseClick(Sender: TObject);
var
  i : Integer;
begin
  Screen.Cursor := crHourGlass;
  for i := Low(fFiles) to High(fFiles) do CloseFile(fFiles[i].AFile);
  btnEnde.SetFocus;
  Screen.Cursor := crDefault;
end;

procedure TForm1.btnEndeClick(Sender: TObject);
var
  Ini : TIniFile;
begin
  Ini := TIniFile.Create(ChangeFileExt(Application.ExeName,'.ini'));
  Ini.WriteString('Config','InputFile',fInputFile);
  Ini.WriteString('Config','OutputFiles',fOutputFiles);
  Ini.WriteInteger('Config','Position',fPosition);
  Ini.Free;
  Close;
end;

end.
Inidatei dazu:
Code:
[Config]
InputFile=I:\rockyou2021.txt
OutputFiles=I:\rockyou2021-%.3d.txt
Position=0
Damit sollte es möglich sein, die Verarbeitung und Aufteilung einer Datei zu starten, beliebig zu unterbrechen und zu einem späteren Zeitpunkt an der Stelle zur Weiterverarbeitung aufzusetzen, an der die vorherige Verarbeitung abgebrochen wurde.

Testdaten:

Dateigröße: 199.526.693 Byte
Zeilen : 3.225.774
Laufzeit : 00:18:11

Festplatte: extern USB 2
Rechner : Dell Optiplex GX620
PentiumR 2 x 2,80 GHZ, 1GB RAM

Hochgerechnet auf 97 GB: ca. 6 bis 7 Tage Laufzeit

Bei einem zeitgemäßen System könnte die Laufzeit durchaus kürzer ausfallen
  Mit Zitat antworten Zitat
GummiKuh68

Registriert seit: 10. Aug 2022
Ort: Berlin
4 Beiträge
 
Delphi 7 Enterprise
 
#14

AW: Große Textdatei - einzelne Zeile löschen

  Alt 14. Aug 2022, 13:23
Moin zusammen,

da ist man mal 3 Tage wettertechnisch (DG!) ausser Gefecht gesetzt....!
Mit der Resonanz hätte ich jetzt nicht gerechnet!!!

Aber eins nach dem anderen.

@Rollo62
Das Komplexe wollte ich weitestgehenst vermeiden - komplex heißt i.d.R. schwer zu Warten!

@himitsu
Ich benutze ein altes Delphi, da ich noch alte Programme von Kunden unterstützen muss,
die kein Geld für große Neuentwicklungen haben bzw. wollen dass alles ohne große Änderungen weiterläuft.
Und was den Speicherverbrauch angeht, so versuche ich diesen auf ein Minimum zu reduzieren, deshalb der
eingeschlagene Weg...der offensichtlich nicht das Optimum zu sein scheint.

@jaenicke
Danke für den Tip, muss ich mir aber auch erstmal anschauen.

@Günther
Nein, ist noch nicht zu spät. Und ja, ich mache die Zieldatei immer wieder auf und zu. Ich weiß das das Zeit kostet - deswegen ja der Thread.

@Monday
Kannte ich noch nicht. Habe aber in einem Test 3,65 GB gut mit Notepad++ öffnen können(Erklärung am Ende).

@dummzeuch
Das klingt interessant - werd ich mir mal anschauen!

@freimatz
Wie gesagt, ich hatte bei Ultraedit alles eingetragen für die Demo und auf Submit geklickt. Aber der Download wurde nie gestartet.

@Delphi.Narium
Tatsächlich handelt es sich um ASCII-11/-12 (VT,FF), ASCII-33 bis (vermutlich) ASCII-126.
Die Dateien vorher anzulegen, wäre natürlich eine Option, die bei diesem Umfang einiges bringt.
Ich sage mir aber auch - ich hab ein Programm und ich habe eine Datei die ich damit bearbeiten will.
Soll heißen, ich schmeiße dem Programm eine Datei hin und das Programm schmeißt mir mein Wunschergebnis
vor die Füße - macht also alles alleine, ohne Vorarbeit.
Dein Beispiel muss ich mir mal in Ruhe anschauen, sieht auf jeden Fall interessant aus.

Ich war natürlich - trotz der gefühlten 500°C in der Wohnung - nicht untätig und hab das Programm mal so laufen lassen.
Die erste große Datei mit 3,65 GB dauerte ewig - und das war erst die erste von vielen!
Dabei fiel mir auf, dass Delphi 7 unter Win10 nur einen Kern benutzt - hätte aber 12 auf dem Entwicklungsrechner.

Also erneut Google bemüht mal was anderes vorzuschlagen und da kam öfter Lazarus ins Spiel.
Ergo Lazarus angesehen und festgestellt, dass man das auch unter Linux nutzen kann.
Also den Pi4 angeschmissen und draufgebügelt, Projekt rübergezogen und kompiliert - schlimmer kann es ja
nicht mehr werden mit der Performance!

Was soll ich sagen - der kleine Pi4 rechnet mit allen Kernen und dadurch natürlich schneller.
Die 3,65 GB hat er nicht wie Delphi in knapp 10 Stunden geschrieben, sondern in 2!
Ich lass das jetzt so laufen (nach 2 Tagen die Hälfte geschafft), weil man den Pi dank leisem Lüfter durchlaufen lassen kann,
suche aber dennoch nach dem Weg, wie man es unter Windows schneller hinbekommt.
Den Pi4 zu benutzen, war ja eher eine Verzweifelungstat als eine Lösung!

Ich schau mir jetzt erstmal die Links/den Code von euch an und hoffe, dass ich die Tage daran weiterarbeiten kann - Urlaub ist leider zu Ende.
To be continued...
Oliver
Viele Grüße

GummiKuh68
  Mit Zitat antworten Zitat
Jumpy

Registriert seit: 9. Dez 2010
Ort: Mönchengladbach
1.737 Beiträge
 
Delphi 6 Enterprise
 
#15

AW: Große Textdatei - einzelne Zeile löschen

  Alt 15. Aug 2022, 08:39
Bzgl. der vorab angelegten Dateien für jeden Buchstaben: Man könnte auch sowas wie das Analogon zu Lazy Loading / Lazy Initialisation vorsehen. Du gibst deine Zeile und den ersten Buchstaben an die Funktion, die das speichern übernehmen soll. Diese hat ein Array oder Liste von allen bereits geöffneten Dateien mit dem ersten Buchstaben als Key, um die Datei in der Liste zu finden und wenn es für den Key noch keine Datei gibt, wird sie einmalig angelegt und geöffnet.
Ralph
  Mit Zitat antworten Zitat
Benutzerbild von dummzeuch
dummzeuch

Registriert seit: 11. Aug 2012
Ort: Essen
1.623 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#16

AW: Große Textdatei - einzelne Zeile löschen

  Alt 15. Aug 2022, 09:18
Dabei fiel mir auf, dass Delphi 7 unter Win10 nur einen Kern benutzt - hätte aber 12 auf dem Entwicklungsrechner.

Ergo Lazarus angesehen und festgestellt, dass man das auch unter Linux nutzen kann.
Also den Pi4 angeschmissen und draufgebügelt, Projekt rübergezogen und kompiliert - schlimmer kann es ja nicht mehr werden mit der Performance!

Was soll ich sagen - der kleine Pi4 rechnet mit allen Kernen und dadurch natürlich schneller.
Ich kann nicht wirklich glauben, dass derselbe Code, der mit Delphi 7 compiliert auf einem Kern läuft, mit Lazarus compiliert plötzlich alle Kerne benutzt. Da musst Du noch mehr geändert haben.
Thomas Mueller
  Mit Zitat antworten Zitat
GummiKuh68

Registriert seit: 10. Aug 2022
Ort: Berlin
4 Beiträge
 
Delphi 7 Enterprise
 
#17

AW: Große Textdatei - einzelne Zeile löschen

  Alt 15. Aug 2022, 22:24
...sorry, mein Fehler!
Falsch geguckt!
War nur irritiert, weil der Prozess in der Tabelle mit nur 10 - 12 % CPU-Zeit angezeigt wurde.
Wenn man sich das pro Kern ansieht, sieht das schon anders aus (s.Anhang).

Dennoch wunderts mich ein wenig, warum der kleine Pi mit allen Kernen rechnet und mit seinen 4x1,5 GHz einen 6x4GHz nass macht!?

Kann doch nicht nur ne Frage von CPU-Architektur und/oder OS-Overhead sein...!?
Miniaturansicht angehängter Grafiken
bild_2022-08-15_222124357.png  
Oliver
Viele Grüße

GummiKuh68

Geändert von GummiKuh68 (15. Aug 2022 um 22:37 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#18

AW: Große Textdatei - einzelne Zeile löschen

  Alt 15. Aug 2022, 22:46
nass macht?

rechnet er wirklich mehr/schneller, oder schiebt er nur regelmäßig den Prozess zwischen den Kernen hin und her, anstatt es auf einem kern zu lassen.


Effektiv macht es keinen großen Unterschied, ob man einen Threat 20 Zyklen auf einem Kern rechnen lässt, oder nacheinander 20 Zyklen auf unterschiedlichen Kernen.
Auf einem Kern wird es nur optimaler, wenn man den Thread zwischen den Zyklen im Kern belassen kann, ohne dass sein Context ausgelagert werden muß. (heißt, er ist auf diesem Kern nahezu alleine)



Bei Singlethread macht es aber einen Unterschied, ob man 8 Kerne a 2 GHz hat, oder 16 Kerne a 1 GHz.
(bei dem Kernewahn mancher Hersteller denkt man es wird besser, je mehr, aber wenn dafür die Kerne jeweils weniger können, dann muß es nicht immer besser sein)



Ich arbeite teilweise mit 63 Kernen ... die knapp 1-2% im Prozess sehen nach nichts aus, aber dennoch ist ein Kern zu 100% ausgelastet und es geht garnicht schneller. (außer man bekommt ein gutes Multithread hin)
$2B or not $2B

Geändert von himitsu (15. Aug 2022 um 22:54 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.648 Beiträge
 
Delphi 11 Alexandria
 
#19

AW: Große Textdatei - einzelne Zeile löschen

  Alt 16. Aug 2022, 08:47
Vielleicht hast du in deinem PC ja noch eine Festplatte statt einer SSD? Dann wäre klar, dass der Pi mit Flashspeicher Vorteile hat...
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
GummiKuh68

Registriert seit: 10. Aug 2022
Ort: Berlin
4 Beiträge
 
Delphi 7 Enterprise
 
#20

AW: Große Textdatei - einzelne Zeile löschen

  Alt 16. Aug 2022, 10:19
@himitsu
naja, wie schon gesagt die 3,65 GB Datei hat auf dem Desktop knapp 10 Stunden gebraucht, auf dem Pi 2!
Ich würde sagen der Pi ist da minimal schneller!

@jaenicke
Ich hab Tatsache noch ne HDD drin, dient aber nur als Ablage für Backups.
Oliver
Viele Grüße

GummiKuh68
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 3     12 3      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:50 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