| Autor |
Nachricht |
AScomp
      
Beiträge: 133
Delphi 5 Prof., Delphi 7 Prof., Delphi 2007, Delphi 2009
|
Verfasst: Mi 07.09.11 12:50
Betrifft: allgemeinen Dateizugriff
Hallo,
folgendes Problem macht mir derzeit zu Schaffen, vielleicht weiß jemand Rat.
Ich möchte den Inhalt einer TStringList (kann mehrere MB an Daten enthalten) in eine Datei mit UTF8-Codierung schreiben, damit Unicode-Zeichen korrekt gespeichert werden. Dabei kann es zu einem EOutOfMemory-Fehler in der Zeile "fProtList.SaveToFile(AFilename, TEncoding.UTF8);" kommen:
MadExcept:
exception class : EOutOfMemory
exception message : Zu wenig Arbeitsspeicher.
main thread ($96c):
0040773f +0013 bkmaker.exe System 2851 +0 @NewUnicodeString
0040796b +000b bkmaker.exe System 2851 +0 @UStrFromPWCharLen
004a6075 +0091 bkmaker.exe Classes TStrings.GetTextStr
004a678a +002e bkmaker.exe Classes TStrings.SaveToStream
004a6723 +0037 bkmaker.exe Classes TStrings.SaveToFile
008bd227 +009b bkmaker.exe UnitMain 580 +11 SaveLogData
Hat jemand eine Idee, woran das liegen könnte?
Herzlichen Dank und viele Grüße
Andy
|
| |
|
|
Werbung ausblenden? Dann registriere Dich kostenlos.
Weitere Gründe für eine Registrierung.
Werbung ausblenden? Dann registriere Dich kostenlos.
Weitere Gründe für eine Registrierung.
|
|
Gausi
      

Beiträge: 8118
Erhaltene Danke: 251
Win XP, Win 7
D7 PE, RAD Studio 2009 Professional
|
Verfasst: Mi 07.09.11 13:06
Kommt der Fehler beim Speichern, oder schon während des Einfügens? Probier mal, vor der Schleife die Kapazität der Liste passend zu setzen, also auf
Dann muss nur einmal ein zusammenhängender Speicherblock für das Array hinter der Liste reserviert werden, und nicht ständig ein neuer, größerer.
_________________ Oel ngati kameie.
|
| |
|
|
Narses
       

Beiträge: 8371
Erhaltene Danke: 244
W2k, WXPpro
TP3 - D7pro
|
Verfasst: Mi 07.09.11 13:08
Moin!
AScomp hat folgendes geschrieben : | | Hat jemand eine Idee, woran das liegen könnte? |
Du hast zu wenig RAM?  Spaß bei Seite.  Du hast bei diesem Ansatz die Daten drei mal im Speicher: - LogData: TLogData
- fProtList: TStringList;
- 004a6075 +0091 bkmaker.exe Classes TStrings.GetTextStr
Weiterhin hast du das Erzeugen der StringListe im try-Block, das ist ein Fehler. Das Erzeugen des Objektes liefert im Fehlerfall eine Exception, aber es wird kein Objekt angelegt, also kannst du es im finally auch nicht wieder freigeben. Wenn du das Erzeugen auch kapseln willst, musst du noch ein weiteres try-except drum rum spendieren.
Ansatz zur Lösung:
Statt eine Stringliste zu erstellen könntest du direkt einen TFileStream aufmachen und die LogData-Elemente (Array?) über einen temporären UTF8-String da rein schreiben. Das sollte Speicher sparen.
//EDIT:
Gausi hat folgendes geschrieben : | | Dann muss nur einmal ein zusammenhängender Speicherblock für das Array hinter der Liste reserviert werden, und nicht ständig ein neuer, größerer. |
Interessanter Ansatz, aber wird das Problem nicht lösen, vermute ich.  Grund: Der Verwaltungsteil einer Stringliste ist auch nur ein Array aus Doppel-Pointern, die Strings selbst liegen ja auf dem Heap. Wenn man die Stringliste in der Kapazität anpasst, wird nur der Pointer-Block neu alloziert, und das sollte nicht so krasse Effekte haben, zumal die Stringliste intern nicht 1-er Schritte beim Vergrößern macht.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
| |
|
|
AScomp 
      
Beiträge: 133
Delphi 5 Prof., Delphi 7 Prof., Delphi 2007, Delphi 2009
|
Verfasst: Mi 07.09.11 13:45
Danke euch, wie von Narses beschrieben werde ich es probieren.
try..finally: Stimmt, TStringList.Create steht nicht im try-Block. Ich ging einfach davon aus, dass das Erstellen einer StringList prinzipiell immer funktioniert - aber natürlich kann selbst das schon scheitern.
|
| |
|
|
AScomp 
      
Beiträge: 133
Delphi 5 Prof., Delphi 7 Prof., Delphi 2007, Delphi 2009
|
Verfasst: Mi 07.09.11 15:23
Hätte da gleich nochmal eine Frage.
Und zwar bin ich auf den TStreamWriter gestoßen, mit dem kann ich die Daten wunderschön und recht flott ohne StringList direkt in eine Datei schreiben mit UTF-8-Codierung:
Allerdings krieg ich das Einlesen nicht hin:
Nach ein paar Aufrufen von SetLength in der while-Schleife kommt eine Zugriffsverletzung. Ich vermute, dass er Probleme damit hat, das Array ständig zu vergrößern. Da ich allerdings Abwärtskompatibilität benötige, kann ich nicht einfach den Count als erste Zeile in die Datei schreiben und somit SetLength nur einmal aufrufen (was fehlerfrei funktionieren würde, bereits getestet).
Hat mir dazu noch jemand einen Tipp?
Gruß
Andy
|
| |
|
|
jaenicke
      
Beiträge: 15840
Erhaltene Danke: 741
XP, W7 x64 (Chrome, IE9, FF), Debian, (OSX 10.7)
RAD XE 2, Java (NB), C++, C# (VS 2010), JS/HTML, PHP, Lazarus
|
Verfasst: Mi 07.09.11 15:59
Setze die Größe nicht in Einzelschritten sondern z.B. immer um 100 hoch, je nach erwarteter Datenmenge. Das lässt sich ja an der Dateigröße abschätzen. Nach Möglichkeit sollte die Schätzung natürlich so sein, dass die Länge genau etwas höher oder gleich der realen Anzahl ist.
Auf die Weise reservierst du seltener neuen Speicher. Und am Ende setzt du die Länge dann auf die reale Größe.
|
| |
|
|
AScomp 
      
Beiträge: 133
Delphi 5 Prof., Delphi 7 Prof., Delphi 2007, Delphi 2009
|
Verfasst: Mi 07.09.11 16:08
Das hatte ich jetzt testhalber ohnehin schon gemacht. Allerdings hatte ich gehofft, dass es noch eine elegantere Lösung gibt.
Hast du noch eine Idee, weshalb es beim ständigen Vergrößern des Arrays zu Zugriffsverletzungen kommt? Gibt es dafür eine plausible Erklärung oder ist es einfach eine zu häufige Speicherreservierung in zu kurzen Zeitabständen?
|
| |
|
|
Gausi
      

Beiträge: 8118
Erhaltene Danke: 251
Win XP, Win 7
D7 PE, RAD Studio 2009 Professional
|
Verfasst: Mi 07.09.11 16:13
Vielleicht solltest du auch zu Beginn die Länge auf 1 setzen, nicht auf 0. Ein Array der Länge 0 hat nämlich gar keinen Eintrag, auch nicht den Nullten.
Du schreibst also immer neben dein Array - das geht wohl eine Zeitlang gut, aber irgendwann knallts halt. 
_________________ Oel ngati kameie.
|
| |
|
|
Narses
       

Beiträge: 8371
Erhaltene Danke: 244
W2k, WXPpro
TP3 - D7pro
|
Verfasst: Mi 07.09.11 16:13
Moin!
AScomp hat folgendes geschrieben : | | Allerdings hatte ich gehofft, dass es noch eine elegantere Lösung gibt. |
Statt Array eine Linked-List nehmen?
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
| |
|
|
AScomp 
      
Beiträge: 133
Delphi 5 Prof., Delphi 7 Prof., Delphi 2007, Delphi 2009
|
Verfasst: Mi 07.09.11 16:30
Danke Gausi, das war's!
-> SetLength(LogData, xLength + 1);
Manchmal sieht man den Wald vor lauter Bäumen nicht. Oder man verwechselt Index mit Count. 
|
| |
|
|
|