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