AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Sonstige Fragen zu Delphi 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen
Thema durchsuchen
Ansicht
Themen-Optionen

32 Bit, TStringList, Textdatei mit 30Mio. Zeilen

Ein Thema von hoika · begonnen am 16. Sep 2015 · letzter Beitrag vom 25. Sep 2015
Antwort Antwort
Seite 3 von 4     123 4      
hoika

Registriert seit: 5. Jul 2006
Ort: Magdeburg
8.275 Beiträge
 
Delphi 10.4 Sydney
 
#21

AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen

  Alt 24. Sep 2015, 15:10
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.
Heiko
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.442 Beiträge
 
Delphi 12 Athens
 
#22

AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen

  Alt 24. Sep 2015, 15:53
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;
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
samso

Registriert seit: 29. Mär 2009
439 Beiträge
 
#23

AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen

  Alt 24. Sep 2015, 17:02
Ah ja, leider vergass ich, dass es sich hier (noch) um D2007 handelt, also keine Generics.
Und deshalb auch kein TStreamReader/TStreamWriter/TEncoding.
  Mit Zitat antworten Zitat
hoika

Registriert seit: 5. Jul 2006
Ort: Magdeburg
8.275 Beiträge
 
Delphi 10.4 Sydney
 
#24

AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen

  Alt 24. Sep 2015, 17:02
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
Heiko
  Mit Zitat antworten Zitat
Benutzerbild von Union
Union

Registriert seit: 18. Mär 2004
Ort: Luxembourg
3.492 Beiträge
 
Delphi 7 Enterprise
 
#25

AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen

  Alt 24. Sep 2015, 17:21
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.
Ibi fas ubi proxima merces
sudo /Developer/Library/uninstall-devtools --mode=all
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#26

AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen

  Alt 24. Sep 2015, 17:24
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.
Kann er nicht wirklich, es sei denn er baut sich auch noch einen virtuellen String-Typen, denn
Hallo,
an einer Stelle wird leider StringList.Text verwendet -> Puff.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von Union
Union

Registriert seit: 18. Mär 2004
Ort: Luxembourg
3.492 Beiträge
 
Delphi 7 Enterprise
 
#27

AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen

  Alt 24. Sep 2015, 17:34
Kann er nicht wirklich, es sei denn er baut sich auch noch einen virtuellen String-Typen, denn
Hallo,
an einer Stelle wird leider StringList.Text verwendet -> Puff.
Kann er doch. Da ja nur die Zeilenadressen sowie ein ggf. mehr oder weniger kleiner Cache im Speicher liegen, spart er auf jeden Fall den Speicher für das Stringarray mit den Texten. Klar macht es "Puff". Aber vermutlich viel später.

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.
Ibi fas ubi proxima merces
sudo /Developer/Library/uninstall-devtools --mode=all
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.442 Beiträge
 
Delphi 12 Athens
 
#28

AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen

  Alt 24. Sep 2015, 17:41
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.
Das Array speichert aber auch nur Pointer auf die Strings. Der Speicher für alle Strings muss also auch nicht in einem Stück vorliegen.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von Union
Union

Registriert seit: 18. Mär 2004
Ort: Luxembourg
3.492 Beiträge
 
Delphi 7 Enterprise
 
#29

AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen

  Alt 24. Sep 2015, 17:58
Das Array speichert aber auch nur Pointer auf die Strings. Der Speicher für alle Strings muss also auch nicht in einem Stück vorliegen.
Dennoch ist 30 M × SizeOf(Pointer) am Stück oft schwieriger vom Memory Manager aufzutreiben als die jeweils nächsten 3 × SizeOf(Pointer).
Ibi fas ubi proxima merces
sudo /Developer/Library/uninstall-devtools --mode=all
  Mit Zitat antworten Zitat
hoika

Registriert seit: 5. Jul 2006
Ort: Magdeburg
8.275 Beiträge
 
Delphi 10.4 Sydney
 
#30

AW: 32 Bit, TStringList, Textdatei mit 30Mio. Zeilen

  Alt 24. Sep 2015, 18:16
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
Heiko

Geändert von hoika (24. Sep 2015 um 18:19 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 3 von 4     123 4      


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 05:34 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