Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Textdatei laden - Integerüberlauf (https://www.delphipraxis.net/204925-textdatei-laden-integerueberlauf.html)

KWin 15. Jul 2020 07:53

Textdatei laden - Integerüberlauf
 
Hallo,

ich versuche mit einer StringList eine relativ große Text-Datei (1*265*819*892 Bytes) zu laden und
bekomme den Fehler "Integerüberlauf".
Compiliert unter Win64 mit Delphi 10.2 Tokyo.

gibt es da eine Lösung für das Problem?

lg Klaus

Dalai 15. Jul 2020 08:58

AW: Textdatei laden - Integerüberlauf
 
Wie sieht dein Code aus? Ohne ist das alles nur Glaskugelgucken.

Grüße
Dalai

KWin 15. Jul 2020 09:02

AW: Textdatei laden - Integerüberlauf
 
Der Code sieht ungefähr so aus:

Delphi-Quellcode:

var
  StrList : TStringList;

  StrList := TStringList.Create;
  try
    StrList.LoadFromFile(LogFileName);

  // ..
  // ..

  except
    on E: Exception do begin
      Info.Add(FormatLang('Fehler beim Öffnen der Datei: %s (%s)',[LogFileName,E.Message]));   // --> Integerüberlauf
    end;

  end;

hoika 15. Jul 2020 09:05

AW: Textdatei laden - Integerüberlauf
 
Hallo,
die TStringList hat eine Grenze von theoretisch 2 Mrd. Einträgen.
Praktisch ist aber schon viel früher Schluss.
Auch damals unter Delphi-64Bit ging es bei mir nicht weiter.

Ich hatte mir damals eine TLargeStringList gebaut,
die intern aus einzelnen StringListen bestand.
Grenze war dann trotzdem bei 1,5 GB Arbeitsspeicher (32-Bit-Delphi).

Bbommel 15. Jul 2020 09:12

AW: Textdatei laden - Integerüberlauf
 
Die Frage ist ja, was du mit dieser riesigen Datei anstellst. Ist es nötig, die komplett in den Speicher zu laden? Oder könntest du sie auch zeilenweise durchgehen und dafür z.B. einen TStreamReader benutzen?

Edit: Ach so, noch vergessen: ich vermute, dass der Fehler vielleicht auch gar nicht direkt in dem LoadFromFile bei dir auftritt, sondern in dem Code dazwischen, den du jetzt hier nicht gezeigt hast, weil du mit einem Integer durch die Zeilen oder Zeichen der StringList gehst oder irgendwas berechnest? Schau doch mal mit dem Debugger, wo der Fehler tatsächlich passiert. Das LoadFromFile selbst würde dir nämlich eher einen vollen Speicher melden, keinen Integerüberlauf (so meine Erfahrung mit zu großen Dateien).

Dalai 15. Jul 2020 09:22

AW: Textdatei laden - Integerüberlauf
 
Zitat:

Zitat von Bbommel (Beitrag 1469585)
ich vermute, dass der Fehler vielleicht auch gar nicht direkt in dem LoadFromFile bei dir auftritt, sondern in dem Code dazwischen, den du jetzt hier nicht gezeigt hast, weil du mit einem Integer durch die Zeilen oder Zeichen der StringList gehst oder irgendwas berechnest? Schau doch mal mit dem Debugger, wo der Fehler tatsächlich passiert.

Dieser Vermutung und der Empfehlung kann ich mich vollumfänglich anschließen.

Grüße
Dalai

Uwe Raabe 15. Jul 2020 09:25

AW: Textdatei laden - Integerüberlauf
 
Zitat:

Zitat von KWin (Beitrag 1469572)
ich versuche mit einer StringList eine relativ große Text-Datei (1*265*819*892 Bytes) zu laden

Das sind ja auch nur gut 180 MB, sollte also kein Problem sein.

Du solltest also wirklich mal den Debugger einsetzen.


Zitat:

Zitat von hoika (Beitrag 1469582)
die TStringList hat eine Grenze von theoretisch 2 Mrd. Einträgen.
Praktisch ist aber schon viel früher Schluss.
Auch damals unter Delphi-64Bit ging es bei mir nicht weiter.

Das LoadFromString/LoadFromStream ist in der Regel für die Begrenzung zuständig.
Delphi-Quellcode:
procedure TStrings.LoadFromStream(Stream: TStream; Encoding: TEncoding);
var
  Size: Integer;
  Buffer: TBytes;
begin
  BeginUpdate;
  try
    Size := Stream.Size - Stream.Position;
    SetLength(Buffer, Size);
    Stream.Read(Buffer, 0, Size);
    Size := TEncoding.GetBufferEncoding(Buffer, Encoding, FDefaultEncoding);
    SetEncoding(Encoding); // Keep Encoding in case the stream is saved
    SetTextStr(Encoding.GetString(Buffer, Size, Length(Buffer) - Size));
  finally
    EndUpdate;
  end;
end;
Obwohl Stream.Size und Stream.Position als Int64 deklariert sind, wird für die Differenz nur ein Integer bereitgestellt. Das nachfolgende SetLength muss dann noch einen zusammenhängenden Speicherbereich in der gewünschten Größe finden, was unter 32-Bit schnell mal zum problem werden kann. Aber auch dieses SetLength beschränkt die Puffergröße auf 2GB, weil NewLength auch nur ein Integer ist.

Allerdings sehe ich in diesem konkreten Fall erstmal keine dieser Grenzen überschritten.

KWin 15. Jul 2020 09:27

AW: Textdatei laden - Integerüberlauf
 
Ich denke auch, dass es nicht möglich ist via TStringlist eine so große Dateien zu laden.

Wenn mann sich die Procedure in System.Classes ansieht dann ist
beim Integer Schluss mit lustig.

Delphi-Quellcode:
procedure TStrings.LoadFromStream(Stream: TStream; Encoding: TEncoding);
var
  Size: Integer;
  Buffer: TBytes;
begin
  BeginUpdate;
  try
    Size := Stream.Size - Stream.Position;
    SetLength(Buffer, Size);
    Stream.Read(Buffer, 0, Size);
    Size := TEncoding.GetBufferEncoding(Buffer, Encoding, FDefaultEncoding);
    SetEncoding(Encoding); // Keep Encoding in case the stream is saved
    SetTextStr(Encoding.GetString(Buffer, Size, Length(Buffer) - Size));
  finally
    EndUpdate;
  end;
end;
Am Code dazwischen liegt es definitiv nicht.
ich werde das ganze umbauen und versuchen mit TStreamReader zu realisieren.

Danke für euer bemühen,
ich werde hier posten, wie ich es gelöst habe.

lg Klaus

KWin 15. Jul 2020 09:47

AW: Textdatei laden - Integerüberlauf
 
Habe das ganze mit TStreamReader gelöst.
Ich durchsuche viele Logfiles mit unterschiedlicher Dateigröße.
Wenn die Datei >= 1073741824 Bytes ist verwende ich den TStreamReader,
ansonsten die TStringList, welche wesentlich schneller ist.

hier mein Codeausschnitt

Delphi-Quellcode:

var
  StreamReader : TStreamReader;
  sLine : string;
  iLine : integer;


begin
  try
    StreamReader := TStreamReader.Create(LogFileName);
    iLine := 0;

    while not StreamReader.EndOfStream do begin
      Inc(iLine);
      sLine := StreamReader.ReadLine;


    // ..
    // ..
    // ..


    end;
  finally
    FreeAndNil(StreamReader);
  end;

end;

Uwe Raabe 15. Jul 2020 10:36

AW: Textdatei laden - Integerüberlauf
 
Zitat:

Zitat von KWin (Beitrag 1469601)
Wenn die Datei >= 1073741824 Bytes ist verwende ich den TStreamReader,
ansonsten die TStringList, welche wesentlich schneller ist.

Aber deine Datei ist doch gar nicht so groß:
Zitat:

Zitat von KWin (Beitrag 1469572)
ich versuche mit einer StringList eine relativ große Text-Datei (1*265*819*892 Bytes) zu laden

1*256*819*892 = 187.020.288

Dalai 15. Jul 2020 10:41

AW: Textdatei laden - Integerüberlauf
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1469611)
Zitat:

Zitat von KWin (Beitrag 1469572)
ich versuche mit einer StringList eine relativ große Text-Datei (1*265*819*892 Bytes) zu laden

1*256*819*892 = 187.020.288

[Korinthe] 1*265*819*892 Bytes = 193.595.220 Bytes [/Korinthe] Ändert aber natürlich nichts an der Größenordnung. Wichtige Teile des Codes fehlen, daher ist eine Beurteilung nicht sinnvoll möglich, woran es genau klemmt.

Grüße
Dalai

Jasocul 15. Jul 2020 10:46

AW: Textdatei laden - Integerüberlauf
 
Ich vermute, dass 1*265*819*892 Bytes eigentlich 1.265.819.892 Bytes heißen soll und der * nur der Verdeutlichung dienen sollte.

KWin 15. Jul 2020 10:50

AW: Textdatei laden - Integerüberlauf
 
Ich habe es ausprobiert,
wenn die Datei > wie MaxInteger/2 ist (1073741824) dann tritt bei StringList.LoadfromFile
der Fehler Integerüberlauf auf. Ich vermute mal, dass Delphi für jedes Character 2 Bytes benötig.

Sherlock 15. Jul 2020 11:08

AW: Textdatei laden - Integerüberlauf
 
Zitat:

Zitat von KWin (Beitrag 1469618)
Ich vermute mal, dass Delphi für jedes Character 2 Bytes benötig.

Je nach Encoding gerne auch mal vier Byte. Das liegt aber nicht an Delphi: https://de.wikipedia.org/wiki/UTF-8#Allgemeines

Sherlock

himitsu 15. Jul 2020 14:35

AW: Textdatei laden - Integerüberlauf
 
Die Datei wird ja quasi mehrfach in den Speicher geladen
* erstmal in einem Puffer
* dann nochmal als Unicode umkopiert
* und zum Schluss nochmal jede Zeile als einzelner String

Die ersten Beiden Zeile sind ein Block und müssen demach auch in einen "goßen" freien Speicherblock
und erfahrungsgemäß wird es ab 700 MB oft nahezu unmöglich im fragmentieren Speicher noch was zu finden.

Lösung: mit 64 Bit kompilieren

Oder Zeilenweise einlesen, entweder über eine passende Streamingfunktion oder mit den alten AssignFile+ReadLn,
aber selbst das wird nicht gehen, denn die Strings sind zwar kurze Zeilen-Stückchen,
die sich im Speicher gut verteilen, aber Dank Unicode sind es dann über 2 GB,
womit du dann die 4GB-Option aktivieren
Delphi-Quellcode:
{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE}
,
so dass du dann bis fast 4 GB (64 Bit Windows), bzw. 3 GB (32 Bit Windows) zur Verfügung hasthättest. (abzüglich geladener EXE, DLLs usw.)




Das "richtige" Verfahren ist aber nicht ALLEs zu laden, sondern solche großen Dateien nur stückchenweise in mehreren Blöcke á einigen KB/MB aufgeteilt zu laden und nacheinander zu behandeln.

haentschman 15. Jul 2020 14:42

AW: Textdatei laden - Integerüberlauf
 
Zitat:

Lösung: mit 64 Bit kompilieren
Zitat:

TE: Compiliert unter Win64 mit Delphi 10.2 Tokyo.
...wer lesen kann ist klar im Vorteil. :zwinker:

Andreas13 15. Jul 2020 15:00

AW: Textdatei laden - Integerüberlauf
 
Zitat:

Zitat von haentschman (Beitrag 1469646)
Zitat:

Lösung: mit 64 Bit kompilieren
Zitat:

TE: Compiliert unter Win64 mit Delphi 10.2 Tokyo.
...wer lesen kann ist klar im Vorteil. :zwinker:

Ich habe den TE so verstanden, daß er zwar 64-Bit-Windows verwendet, aber nicht die Delphi-Zielplattform 64-Bit benutzt hat.
Gruß, Andreas

KWin 15. Jul 2020 15:58

AW: Textdatei laden - Integerüberlauf
 
Hallo,

ich verwende 64-Bit-Windows und die Delphi-Zielplattform 64-Bit.

himitsu 15. Jul 2020 17:55

AW: Textdatei laden - Integerüberlauf
 
Wieviele Zeilen hatt denn die Datei?

[EDIT]
Obwohl, selbst mit 32 Bit sind ja theoretisch schon knapp 'ne milliarde Zeilen möglich
und selbst wenn im 64 Bit noch ein Zähler auf 32 Bit stehen würde, wären es immernoch mindestens 500 Mio.

Uwe Raabe 15. Jul 2020 18:53

AW: Textdatei laden - Integerüberlauf
 
Mit LoadFromFile ist die Größe der Datei intern auf 2GB beschränkt, unabhängig von den Zeilen und von 32/64-Bit.

Ein alternative Ansatz dazu wäre über einen StreamReader die Datei zeilenweise zu lesen und jede Zeile mit Add anzufügen.

Michael II 15. Jul 2020 19:02

AW: Textdatei laden - Integerüberlauf
 
Leider kann ich mit 10.4 nicht mehr Win64Bit debuggen - es steht immer sofort "disconnected session" - und das war's. Und ein anderes Delphi habe ich grad nicht.

Aber laufen lassen kann ich 64 Bit Programme: Wenn ich zu einer TStringList 50 Mio Mal den string '012345678901234567890123456789' hinzufüge, dann klappt dies. Wenn ich dann savetofile aufrufe, dann gibt's auch hier einen Integerüberlauf.

Der von dir erwähnte Integerüberlauf tritt also nicht nur beim Laden, sondern auch beim Speichern auf.

himitsu 15. Jul 2020 23:23

AW: Textdatei laden - Integerüberlauf
 
Der 64 Bit-Debugger wird, wie der externe RemoteDebugger, über eine lokale TCP/IP-Verbindung angesprochen ... schau mal ob rmtdbg270.exe lokal freigegeben ist.


So, ansonsten du hast Pech.
Im SaveToFile wird erst alles in einen String kopiert
und strings können maximal 2 GB groß werden.

Bei DynArrays ist Length als NativeInt deklariert, aber bei den LongStrings (z.B. String/UnicodeString) nur als Integer.

TStrings.SaveToStream: erst über TStrings.GetTextStr und dann durch TEncoding.GetBytes in ein Byte-Array, welcher in den Stream kopiert wird
und der String ist hier die Limitierung.
Beim LoadFromFile andersrum.


Du darfst gern eine TStringList benutzen, aber nur für Dateien unter 1 GB,
oder du implementierts die Lade-/Speichernfunktion selbst.

Oder machst es eben "richtig".
Zitat:

Das "richtige" Verfahren ist aber nicht ALLEs zu laden, sondern solche großen Dateien nur stückchenweise in mehreren Blöcke á einigen KB/MB aufgeteilt zu laden und nacheinander zu behandeln.
Sieh dir HxD an, da hast ein Beispiel dass man auch kleine Terrabytedateien laden kann.

bernau 16. Jul 2020 10:26

AW: Textdatei laden - Integerüberlauf
 
Zitat:

Zitat von himitsu (Beitrag 1469685)
Oder machst es eben "richtig".

Definiere "richtig".

Der TE hat nicht geschrieben, was er mit der Textdatei machen möchte.

Wenn zur Auswertung der Textdatei wahllos zwischen den Zeilen hin und her gesprungen werden muss, dann bleibt nichts anderes übrig, als die Datei komplett einzulesen. Ausser die Textdatei hat eine feste Zeilenlänge. Aber auch das weist du nicht.

Bbommel 16. Jul 2020 10:33

AW: Textdatei laden - Integerüberlauf
 
Der TE hat doch schon längst geschrieben, dass er große Dateien jetzt zeilenweise per StreamReader einliest. :-)

himitsu 16. Jul 2020 14:24

AW: Textdatei laden - Integerüberlauf
 
Zitat:

Wenn zur Auswertung der Textdatei wahllos zwischen den Zeilen hin und her gesprungen werden muss, dann bleibt nichts anderes übrig, als die Datei komplett einzulesen.
In den dem Fall würde ich die Datei eher in den Speicher mappen, anstatt reinzuladen,
da hier der FileCache von Windows ins Spiel kommt und diese Datei nicht "komplett" im RAM/Auslagerungsdatei abgelegt wird.
MemoryMappedFiles

Bei sequentiell ist ein StreamReader aber bestimmt einfacher.

Wobei, bei der MMF kann man da den Anfang und Ende als PAnsiChar/PWideChar casten und kann gemütlich dazwischen durch den Arbeistspeicher hüpfen, während sich Windows um das Laden kümmert (bzw. Entladen, wenn freier RAM benötigt wird).
z.B. der Code oder die Ressourcen von EXE und DLL sind ebenso in den Speicher gemappt, so dass es nicht in die Auslagerungsdatei muß, da es von der Platte geladen und notfalls entladen werden kann. Abgesehn davon wo Code/Speicher/Adressen angepasst wurden, dann wird der Teil von der Datei entkoppelt (CopyOnWrite) und bleibt im RAM/Auslagerungsdatei.

hoika 16. Jul 2020 19:11

AW: Textdatei laden - Integerüberlauf
 
Hallo,
wenn das SaveToFile nicht klappt,
dann schreib es per AssignFile, WriteLn (Typ: Text) usw.;

Michael II 16. Jul 2020 20:02

AW: Textdatei laden - Integerüberlauf
 
Hallo in die Runde... nur kurz: Ich will gar nicht viele Elemente einer TStringlist speichern. Es war nur eine Bemerkung, dass sowohl das Laden via loadfromfile (ursprüngliches Thema des Threads) wie eben auch das Speichern via savetofile nicht klappt. Grund ist klar (siehe oben).

[ Danke himitsu für deinen 64Bit Win Debugger Firewall Tipp (Einstellung war und ist OK) - wenn ich keine Lösung finde, dann muss ich wohl mal ein neues Thema eröffnen. ]

TurboMagic 16. Jul 2020 21:35

AW: Textdatei laden - Integerüberlauf
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1469591)
Das LoadFromString/LoadFromStream ist in der Regel für die Begrenzung zuständig.
Delphi-Quellcode:
procedure TStrings.LoadFromStream(Stream: TStream; Encoding: TEncoding);
var
  Size: Integer;
  Buffer: TBytes;
begin
  BeginUpdate;
  try
    Size := Stream.Size - Stream.Position;
    SetLength(Buffer, Size);
    Stream.Read(Buffer, 0, Size);
    Size := TEncoding.GetBufferEncoding(Buffer, Encoding, FDefaultEncoding);
    SetEncoding(Encoding); // Keep Encoding in case the stream is saved
    SetTextStr(Encoding.GetString(Buffer, Size, Length(Buffer) - Size));
  finally
    EndUpdate;
  end;
end;
Obwohl Stream.Size und Stream.Position als Int64 deklariert sind, wird für die Differenz nur ein Integer bereitgestellt. Das nachfolgende SetLength muss dann noch einen zusammenhängenden Speicherbereich in der gewünschten Größe finden, was unter 32-Bit schnell mal zum problem werden kann. Aber auch dieses SetLength beschränkt die Puffergröße auf 2GB, weil NewLength auch nur ein Integer ist.

Allerdings sehe ich in diesem konkreten Fall erstmal keine dieser Grenzen überschritten.

Wäre das aber nicht trotzdem ein Kandidat für eine kleine Codeänderung durch EMBT?

Uwe Raabe 16. Jul 2020 21:45

AW: Textdatei laden - Integerüberlauf
 
Zitat:

Zitat von TurboMagic (Beitrag 1469772)
Wäre das aber nicht trotzdem ein Kandidat für eine kleine Codeänderung durch EMBT?

Aber natürlich!

himitsu 16. Jul 2020 21:53

AW: Textdatei laden - Integerüberlauf
 
Nur an der Stelle das zu Ändern ist völlig irrelevant, so lange es durch einen String gejagt wird, dann dafür ist hier der Integer als Typ vollkommen richtig, siehe mein Post.


PS, was CodeInsight für Typen beim SetLength anzeigt, ist auch irrelevant, verwirrend und falsch,
denn siehe DynArraySetLength in System.pas ist es für dynamische Arrays richtig als NativeInt deklariert,
aber eben bei den LongStrings immernoch als Integer. (sowohl Funktion, als auch im Typ)
Bezüglich der Compiler-Magic darfst eh nie dem trauen, was dir angezeigt wird.

Was extrem schwachsinnig ist, denn in 32 Bit sind seit jeher DynArray und LongString vom Aufbau her rundlegend kompatibel. (auch dort wo seit 2009 die CodePage und CharSize sich verstecken, ist im DynArray ein Platzhalter enthalten), was nun im 64 Bit aber nicht mehr passt. Und das hier auch zu diesem Problem führt.

Uwe Raabe 16. Jul 2020 22:07

AW: Textdatei laden - Integerüberlauf
 
Natürlich würde man den Code so ändern, daß eben nicht der eine Gesamtstring das Nadelöhr ist, sondern den Stream z.B. über einen TStreamReader zeilenweise einlesen - eventuell auch erst ab einer gewissen Streamgröße. Beim Speichern natürlich analog mit einem TStreamWriter.


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