![]() |
AW: Große Textdatei - einzelne Zeile löschen
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:
einsparen.
if FileExists(OFile) then
begin assignFile (OutFile, OFile); append(OutFile); end else 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; |
AW: Große Textdatei - einzelne Zeile löschen
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 :oops: [/edit] egal: mit Win64 kompiliert und ausreichend Auslagerungsdatei (und natürlich freie Festplatte dafür) oder ein kleiner 128GB oder 256GB RAM-Riegel ![]() * 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. |
AW: Große Textdatei - einzelne Zeile löschen
Mal so JustForFun eine Variante mit FileStream und den alten Dateifunktionen aus Pascalzeiten.
Delphi-Quellcode:
Inidatei dazu:
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.
Code:
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.
[Config]
InputFile=I:\rockyou2021.txt OutputFiles=I:\rockyou2021-%.3d.txt Position=0 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 ;-) |
AW: Große Textdatei - einzelne Zeile löschen
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... |
AW: Große Textdatei - einzelne Zeile löschen
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.
|
AW: Große Textdatei - einzelne Zeile löschen
Zitat:
|
AW: Große Textdatei - einzelne Zeile löschen
Liste der Anhänge anzeigen (Anzahl: 1)
...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...!? |
AW: Große Textdatei - einzelne Zeile löschen
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) |
AW: Große Textdatei - einzelne Zeile löschen
Vielleicht hast du in deinem PC ja noch eine Festplatte statt einer SSD? Dann wäre klar, dass der Pi mit Flashspeicher Vorteile hat...
|
AW: Große Textdatei - einzelne Zeile löschen
@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. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:29 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz