32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo,
für einen Import einer Textdatei (vordefiniertes Format) lade ich diese Textdatei per StringList.LoadFromFile. Hier schmiert er mir mit "Arbeitsspeicher voll" ab. Die Lösung per TextFile bringt es auf 19 Mio. Einträge. Gibt es eine fertige Alternative zur TStringList? Ich will jetzt nicht den ganzen Code ändern, der sequentiell durch die Liste läuft. Danke Heiko |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
19 Mio. Zeilen und wie viel MB hat die Datei?
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Wäre das gute alte Text-File mit ReadLn vielleicht eine Alternative - oder wird da auch der gesamte Inhalt gepuffert?
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Noch bessser in einem Stream.
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Und was kann der Nutzer mit 19 Millionen Zeilen anfangen, wenn er doch so wie so nur 50 sieht? :?
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo,
Der Nutzer sieht gar nichts, der Code ist halt auf TStringList zugeschnitten, also wahlfreier Zugriff, nicht nur in einer Richtung. Das ReadLn mit TextFile sorgt überhaupt erst mal, dass überhaupt etwas in die TStringList kommt. Ich baue mir jetzt was auf Basis von array of String. Heiko |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Denkbar ungünstig eine visuelle Komponente zur Datenhaltung zu nutzen. Du hast ja gerade ein gutes Beispiel gegeben. :wink:
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
Wenn Du z.B. ein Log verarbeiten willst, ist readln garnicht mal so dumm, dann werden überflüssige Zeilen z.B. gleich beim Einlesen verworfen. Gruß K-H P.S. Zitat:
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo,
ich lese eine Datei in eine StringList ein. Es ist eine BDT-Datei (Medizintechnik). Sie besteht intern aus Blöcken (10-1000 Zeilen sind ein Block). Ich suche mir bestimmte Blöcke (Block A) raus, mache was (Import), fange wieder von vorn an für andere Blöcke, die erst nach dem Import der A-Blöcke gültig sind usw. Ich will den Code nicht ändern. Ich habe mir jetzt eine TLargeStringList=class(TStrings) gebaut, muss nur noch überall TStringList durch TStrings ersetzen und hoffen ... Hauptziel: den Code nur minimal ändern. Heiko |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Da lässt sich noch viel machen :-D
Die Position der Zeilenanfänge kann man nach einem ersten Scan im Speicher halten, das wäre so um die 24 MiB. Dazu kann man einen Zeilen-Cache bauen, dass mit den Blöcken spricht ja für Lokalität im Algorithmus und die 1000 Zeilen kannst du leicht im Speicher halten. Das Ganze hinter
Delphi-Quellcode:
zu verstecken sollte dann nicht so schwer sein.
TStrings
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo,
die Größe ist 500 MB, könnten aber auch 5 GB werden. Das ist extrem alter Code, klar könnte ich was ändern, aber warum, es gibt genügend andere Stellen ... Heiko |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Wenn du nur sequentiell durch die Zeilen gehst, wäre da ein
Delphi-Quellcode:
nicht ausreichend?
TEnumerable<string>
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo,
an einer Stelle wird leider StringList.Text verwendet -> Puff. Ah ja, leider vergass ich, dass es sich hier (noch) um D2007 handelt, also keine Generics. Aber wie gesagt, es läuft. Morgen kommen noch ein paar Unit-Tests dazu, dann zeig ich mal die neue Unit LargeStringList.pas ( ;) ). Heiko |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Das sieht man mal, wie praktisch es ist, nur gegen ein Interface zu programmieren (hier: Basisklasse).
Wenn die verwendete Implementierung dann nicht mehr ausreicht: Wupps! Ausgetauscht, läuft! |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
Da würde ich eher auf eine andere Lösung setzen und eine Klasse erstellen, die so ähnlich wie
Delphi-Quellcode:
arbeitet aber eben kein
TStrings
Delphi-Quellcode:
ist.
TStrings
Ok, bevor wir meckern, warten wir auf die Vorstellung von
Delphi-Quellcode:
:)
TLargeStringList = class( TStrings )
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Ganz ehrlich: Ich würde den bisherigen Importer eliminieren und das ganze neu (und vor allem sauber) implementieren. Als das damals gebaut wurde, waren 5MB vielleicht große Dateien. Solche Files kann man mit TStringList noch einigermaßen handeln, aber alles darüber wird zur Laufzeit-Qual. Heute sind 5GB große Files kein Thema mehr und in 8 Jahren sind es 5TB große Files. kommt deine LargeStringList damit auch klar, ohne vom Benutzer verlangen zu müssen, er soll n paar RAM-Riegel nachstecken ? :-D
Letztendlich versuchst du, denselben Unsinn (Datei komplett im Speicher) nur auf anderem Weg zu implementieren. Das wird vermutlich scheitern oder bestenfalls eine wenig effiziente Lösung werden. Besser, du investierst deine Zeit in eine Art Parser, der einmal durch die Datei durchgeht, in verschiedenen Listen sich merkt, in welchen Zeilen welcher Satz steht, die A-Sätze schon mal verarbeitet und im zweiten Lauf dann die anderen Sätze dazusortiert. Dann ist es auch fast egal, ob das File 5MB, 5GB oder 5PB groß ist. Solche Umbau-Arien sind normal, wenn man alte bis sehr alte Projekte bearbeitet. Ich mach das seit Jahren und treffe immer wieder auf solche Stolperfallen. |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
In "sehr alten" Projekten wird wahrscheinlich keine Stringlist eingesetzt, darum laufen die wohl immer noch:stupid:
Gruß K-H |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Dann ist es aber wohl kein Delphi-Programm ;) TStringList gibt es schon seit D2 (weiter hatte ich keine Lust zu recherchieren). Ich denke eher, das in so einem "sehr alten" Projekt jemand saß, der etwas weiter als 2-3 Jahre gedacht hat ;)
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo,
nix gegen den, der das damals so programmiert hat ... (ich war es nicht). Hier meine Unit. Heiko
Delphi-Quellcode:
unit LargeStringList;
interface uses Classes; type TLargeStringList = class(TStrings) private FItems: array of String; function GetField(Index: Integer): String; public property Strings[Index: Integer]: String read GetField; default; constructor Create; destructor Destroy; override; function Count: Integer; function Add(const S: String): Integer; override; procedure Clear; override; procedure LoadFromFile(const DateiName: String); override; procedure SaveToFile(const DateiName: String); override; procedure AppendToFile(const DateiName: String); function Text: String; function TextKurz: String; function Get(Index: Integer): string; override; function GetCount: Integer; override; procedure Delete(Index: Integer); override; procedure Insert(Index: Integer; const S: String); override; end; implementation uses SysUtils; { TLargeStringList } function TLargeStringList.Add(const S: String): Integer; var iLaenge: Integer; begin iLaenge := Length(FItems); SetLength(FItems, iLaenge+1); FItems[iLaenge] := S; Result := iLaenge; end; procedure TLargeStringList.Clear; begin FItems := nil; end; function TLargeStringList.Count: Integer; begin Result := Length(FItems); end; constructor TLargeStringList.Create; begin inherited ; FItems := nil; end; destructor TLargeStringList.Destroy; begin FItems := nil; inherited; end; function TLargeStringList.Get(Index: Integer): string; begin Result := FItems[Index]; end; function TLargeStringList.GetCount: Integer; begin Result := Length(FItems); end; procedure TLargeStringList.Delete(Index: Integer); begin raise Exception.Create('nicht implementiert'); end; procedure TLargeStringList.Insert(Index: Integer; const S: String); begin raise Exception.Create('nicht implementiert'); end; function TLargeStringList.GetField(Index: Integer): String; begin Result := FItems[Index]; end; procedure TLargeStringList.LoadFromFile(const DateiName: String); var TextDatei: TextFile; Puffer: array[0..16384] of AnsiChar; sZeile: String; begin FItems := nil; System.SetTextBuf(TextDatei, Puffer); AssignFile(TextDatei, DateiName); Reset(TextDatei); try while not Eof(TextDatei) do begin ReadLn(TextDatei, sZeile); Add(sZeile); end; finally CloseFile(TextDatei); end; end; procedure TLargeStringList.SaveToFile(const DateiName: String); var TextDatei: TextFile; Puffer: array[0..16384] of AnsiChar; iZeile: Integer; sZeile: String; begin System.SetTextBuf(TextDatei, Puffer); AssignFile(TextDatei, DateiName); Rewrite(TextDatei); try for iZeile := 0 to Count-1 do begin sZeile := Strings[iZeile]; WriteLn(TextDatei, sZeile); end; finally CloseFile(TextDatei); end; end; procedure TLargeStringList.AppendToFile(const DateiName: String); var TextDatei: TextFile; Puffer: array[0..16384] of AnsiChar; iZeile: Integer; sZeile: String; begin if FileExists(DateiName) then begin System.SetTextBuf(TextDatei, Puffer); AssignFile(TextDatei, DateiName); System.Append(TextDatei); try for iZeile := 0 to Count-1 do begin sZeile := Strings[iZeile]; WriteLn(TextDatei, sZeile); end; finally CloseFile(TextDatei); end; end else begin SaveToFile(DateiName); end; end; function TLargeStringList.Text: String; var iZeile: Integer; S: String; begin S := ''; for iZeile := Low(FItems) to High(FItems) do begin S:= S+#13#10+FItems[iZeile]; end; Result := S; end; function TLargeStringList.TextKurz: String; var iZeile: Integer; S: String; begin S := ''; for iZeile := Low(FItems) to High(FItems) do begin S:= S+#13#10+FItems[iZeile]; if iZeile>1000 then begin break; end; end; Result := S; end; end. |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Das hättest du aber einfacher haben können. Deine Implementierung ist weitestgehend identisch mit TStringList, lediglich das LoadFromFile und SaveToFile gehen etwas sparsamer mit dem Speicher um. In der bordeigenen StringList wird die Datei zunächst in einen lokalen Buffer gelesen und dann erst in die StringList übertragen. Das kann bei einer sehr großen Datei in 32-Bit schon mal zu einem Out-Of-Memory führen. Die folgende Implementierung geht da etwas sparsamer mit dem Speicher um.
Es bleibt aber immer noch das Problem, daß alle Zeilen überhaupt in den Speicher passen müssen. Bedenkt man, daß intern mit 2 Byte pro Zeichen zu rechnen ist, die Datei aber womöglich in ANSI oder UTF-8 codiert ist, kann das schon bei einer ca. 1 GB großen Datei zum Problem werden. Bei 30 Millionen Zeilen bleiben da pro Zeile auch nur ca. 30 Zeichen. Ab da sollte man spätestens über eine virtuelle TStrings-Implementation nachdenken.
Delphi-Quellcode:
type
TMemorySparingStringList = class(TStringList) public procedure LoadFromStream(Stream: TStream; Encoding: TEncoding); override; procedure SaveToStream(Stream: TStream; Encoding: TEncoding); override; procedure AppendToFile(const DateiName: String); end; procedure TMemorySparingStringList.AppendToFile(const DateiName: String); var writer: TStreamWriter; I: Integer; begin writer := TStreamWriter.Create(DateiName, true); try for I := 0 to Count - 1 do begin writer.WriteLine(Strings[I]); end; finally writer.Free; end; end; procedure TMemorySparingStringList.LoadFromStream(Stream: TStream; Encoding: TEncoding); var reader: TStreamReader; begin BeginUpdate; try reader := TStreamReader.Create(Stream, Encoding); try while not reader.EndOfStream do begin Add(reader.ReadLine); end; finally reader.Free; end; finally EndUpdate; end; end; procedure TMemorySparingStringList.SaveToStream(Stream: TStream; Encoding: TEncoding); var writer: TStreamWriter; I: Integer; begin writer := TStreamWriter.Create(Stream, Encoding); try for I := 0 to Count - 1 do begin writer.WriteLine(Strings[I]); end; finally writer.Free; end; end; |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo,
also, ein Stream bringt mir auch sofort den Fehler. Dass der Algorithmus mal geändert werden muss, ist klar, aber wir haben hier genug andere Sachen zu tun. Ich werde eh irgendwann auf 64-Bit umsteigen, dann ist meine TLargeStringList "open end". Danke Heiko |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Vielleicht könntest Du das Ganze virtualisieren. Die Leseroutine merkt sich nur Anfangsposition und Länge jeder Zeile der Textdatei. Das eigentliche Einlesen der Textzeilen erfolgt dann bei Zugriff. Falls Zeilen mehrfach verarbeitet werden müssen, könnten deren Texte zur Performanceverbesserung in einem Ringspeicher gecached werden.
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
Zitat:
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
Zumal ich mir die Frage stelle, welchen sinvollen Einsatzzweck der Abruf des gesamten strings haben sollte. Weitere Möglichkeit: Eine Stringlist, die nicht mit einem Array als Backend arbeitet, sondern mit einer doppelt verketteten Liste. Die verbraucht zwar letztendlich insgesamt etwas mehr Speicher - aber kommt mit Fragmentierung zurecht. |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo,
übrigens bekomme ich die Datei unter XE4 64-Bit auch nicht per StringList.LoadFromFile auf (*wunder*) Über dem Umweg per AssignFile(TextFile) und TStringList konnte ich immerhin 19 Mio Einträge erzeugen, ohne "out of memory", 30 habe ich aber. Nachdem jetzt meine TLargeStringList benutze, bekomme ich die Datei wenigstens auf ... Und nochmal: Es ging mir erstmal darum, den Algorithmus möglichst nicht zu ändern. Das .Text wird übrigens für ein Pos benutzt, das könnte ich zur Not natürlich auch als Schleife machen, fällt mir gerade auf, dann geht der Verbrauch nicht so hoch. Gleich mal bauen ... Heiko |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
Zitat:
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
Zitat:
Delphi-Quellcode:
;)
TStringEnumerable
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo,
hm, ich benutze TextKurz (gerade geprüft), also brauche ich das .Text, also den kompletten String gar nicht. Heiko |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo Uwe,
hm? 64 Bit ist stärker als 32 Bit !!! Hast du einen Infolink dazu ? Heiko |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
String-Typen (Delphi) Interne Datenformate (Delphi) - Lange String-Typen Die Länge in Zeichen eines Strings ist in x32- und x64 in den 4 Byte vor dem Inhalt abgelegt. Das allein beschränkt den String auf theoretisch maximal 4 Giga-Zeichen. Die max. ~2 GB Speicher sind eine Beschränkung des/der Compiler. |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo,
netter Link, oha ... also .Text nicht verwenden. Danke! Heiko |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Ich verbitte mit den Missbrauch meines Namens (Puff)! :evil: In der DP macht eine StringListe nicht so einfach "puff"!
|
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Hallo,
dann eben Argzzz :) Heiko |
AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:21 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