Autor |
Beitrag |
zehgeh
Hält's aus hier
Beiträge: 7
Windows 2000 SP4+
Delphi 5 Prof.
|
Verfasst: Do 22.09.05 01:40
Moin, moin.
Situation: Es sollen Strings in einen Memorystream kopiert werden, ohne Write/WriteBuffer zu verwenden. Das mache ich über Pointer, da es um ein vielfaches schneller geht.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| var p, q: PChar; / p zeigt auf aktuelle, q auf letzte Position im Stream s: String; Buffer: TMemoryStream;
p := Buffer.Memory; q = p;
Move(Pointer(s), p^, Length(s)); inc(p, Length(s)); |
Das klappt auch wunderbar. Nun geht es aber um den Speicherbedarf. Mir ist nicht bekannt, wieviel der gemeine User später einmal abzuspeichern hat, darum dachte ich daran, die Anpassung der Streamgröße dynamisch zu gestalten:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| if q - p < 10192 then begin Buffer.SetSize(Buffer.Size + 1024 * 512); q := Buffer.Memory; inc(q, Buffer.Size) end; |
Hmm, vorhin habe ich immer Schreib/Lesefehler gehabt, jetzt heißt es nun 'Out of memory' (Kann aber nicht sein). Was meint Ihr, das sollte doch eigentlich funktionieren?
Viele Grüße und danke für das Anschauen,
Christian
|
|
Heiko
      
Beiträge: 3169
Erhaltene Danke: 11
|
Verfasst: Do 22.09.05 07:32
Hast du mal übrprüft ob er wirklich etwas schreibt? Und kannst du vlt. den gesamten Quelltext der Prozedur geben, also inkl. der Schleife die du hier ausgespart hast? Was mich wundert ist die Zeile q = p;, denn = nimmt man ja eigentlich nur zum Vergleichen. Ich vermute mal du hast den : vergessen 
|
|
zehgeh 
Hält's aus hier
Beiträge: 7
Windows 2000 SP4+
Delphi 5 Prof.
|
Verfasst: Do 22.09.05 08:17
Hallo.
In der Tat hatte ich einen kleinen Fehler drinnen, der sich im richtigen Programm aber nicht auswirkte - glaube ich zumindest. Hier mal das gleiche, nur gekürzt. Ein Button und zwei Memos notwendig (Memo1 -> Geladene Datei, Memo2 -> Ausgabe). Es wird eine Textdatei eingelesen - siehe erste Zeile Quelltext), anschließend werden Return (#13#10) nach #10 konvertiert.
Der Fehler tritt immer noch auf, wenn der Speicherbedarf des Streams erhöht werden soll. Und das wird er gleich am Anfang, da nach der Initialisierung die Größe auf Null feststeht.
Achja, die Daten werden korrekt geändert und geschrieben - wenn ich die Größe *vorher* festsetze, ansonsten kommt halt der Fehler. Und das verstehe ich nicht. Denn die Größe festsetzen tue ich jetzt auch zuerst, nur halt innerhalb der Schleife.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48:
| procedure TForm1.Button1Click(Sender: TObject); var Buffer: TMemoryStream; p, q : PChar; i : Word; s : String; begin Memo1.Lines.LoadFromFile('C:\boot.ini'); Memo1.Lines.Text := Trim(Memo1.Lines.Text); if Memo1.Lines.Count = 0 then Exit;
Buffer := TMemoryStream.Create; try with Memo1 do begin p := Buffer.Memory; q := p; for i := 0 to Lines.Count - 1 do begin if q - p < 10 * 1024 then begin Buffer.SetSize(Buffer.Size + 1024 * 512); q := Buffer.Memory; inc(q, Buffer.Size) end; s := Lines[i];
Move(s[1], p^, Length(s)); inc(p, Length(s));
p^ := #10; inc(p); end end; Buffer.SetSize(p - Buffer.Memory); p := Buffer.Memory; Memo2.Lines.SetText(p) finally Buffer.Free end end; |
Vielen Dank für Eure Blicke.
Christian
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Do 22.09.05 09:44
Delphi-Hilfe:
Zitat: |
Die Methode SetSize setzt die Eigenschaft Size des Speicher-Streams.
procedure SetSize(NewSize: Longint); override;
Beschreibung
Mit SetSize legt man die Größe eines Speicher-Streams fest, bevor dieser mit Daten gefüllt wird. Maßgebend ist der Wert des Parameters NewSize. Vor der Reservierung wird der aktuelle Inhalt von Memory verworfen. Der Speicherpuffer muß somit nicht freigegeben werden.
SetSize wird aufgerufen, bevor der Speicher-Stream mit Daten aus verschiedenen Quellen oder mit dem Teilinhalt eines anderen Streams gefüllt wird. Wenn der vorgesehene Inhalt des Streams exakt mit dem Inhalt eines anderen Streams oder einer Datei übereinstimmt, verwenden Sie statt dessen die Methoden LoadFromStream oder LoadFromFile.
|
Das heißt, dass nicht sichergestellt werden kann, dass der bisherige Inhalt erhalten bleibt, wenn SetSize aufgerufen wird! Inkrementelles Anpassen geht deshalb nicht!
Wenn der Speicher durch Zufall am Ende des bisher genutzten Blocks liegt und in diesem Block genug Speicher ist, um den neuen Bereich aufzunehmen, dann funktioniert das sogar, denn dann beginnt der Speicher an der alten Adresse und hat eine größere Kapazität. Das kann aber nicht vorausgesetzt werden!!!
|
|
zehgeh 
Hält's aus hier
Beiträge: 7
Windows 2000 SP4+
Delphi 5 Prof.
|
Verfasst: Do 22.09.05 10:51
Hallo.
Delphi Hilfe zu TMemoryStream hat folgendes geschrieben: |
Use SetSize to set the Size of a memory stream before filling it with data. SetSize allocates the memory buffer to hold NewSize bytes, preserving as much of the existing data as possible. |
Wenn ich den Bereich also vergrößere, bleibt das davor erhalten. Nichtsdestotrotz habe ich ja das Problem, dass ich den Speicher ja auch gleich am Anfang der Schleife zuweise...
Christian
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Do 22.09.05 10:52
Hi ZehGeh,
vielleicht kannst Du uns(mir) mal erklären, was Du damit bezweckst. Denn ich verstehe den Sinn von dieser Zweckentfremdung eines Streams nicht.
Danke
_________________ Na denn, dann. Bis dann, denn.
|
|
zehgeh 
Hält's aus hier
Beiträge: 7
Windows 2000 SP4+
Delphi 5 Prof.
|
Verfasst: Do 22.09.05 11:01
Hi.
Es geht lediglich darum, für mich interessantes Zeugs aus Textdateien zu extrahieren und dann neu abzuspeichern. Es werden Zeichenketten und Zahlen benötigt, wobei mit letzteren im Programm noch gearbeitet wird. Schlußendlich ein Log-Analysierungsprogramm. Ich hatte das schon mal auf Basis einer TStringList, aber das war Speicherintensiv und langsam. Mit dem MemoryStream bin ich sehr zufrieden, bis auf das angesprochene Problem. Muss mal schauen, ob ich mit GetMem weiterkomme. Unter DOS (lang ist's her), hatte ich mal eine Assembler Funktion zum Vergrößern des Speicherbereichs, ohne überschreiben. Das sollte heute eigentlich auch noch gehen.
Christian
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Do 22.09.05 11:14
zehgeh hat folgendes geschrieben: | Hallo.
Delphi Hilfe zu TMemoryStream hat folgendes geschrieben: |
... preserving as much of the existing data as possible. |
|
Das heißt für mich nur: Wenn ich den Bereich vergrößere, bleibt der bisherige Inhalt manchmal erhalten, manchmal aber auch nicht.
|
|
zehgeh 
Hält's aus hier
Beiträge: 7
Windows 2000 SP4+
Delphi 5 Prof.
|
Verfasst: Do 22.09.05 11:27
Und logisch heißt es, dass der Bereich lediglich überschrieben wird, wenn der Bereich *verkleinert* wird. Meine Logik voruasgesetzt. Aber mal abgesehen vom Inhalt des Speicherbereiches: Wenn ich die Speicherreservierung aus der Schleife entferne und stattdessen einen überdimensionierten Speicherblock vorher reserviere, funktioniert alles. Wenn ich aber innerhalb der Schleife Speicher reserviere - ich greife noch *nicht* darauf zu - kommt es zum Fehler. Und durch den Vergleich q - p < sonstwas muss sofort nach dem Start in die Speicherreservierung gesprungen werden - das klappt auch. Ich habe es bisher nicht mit den Originaldatenmengen getestet, die inkrementelle Erhöhung wird also nur einmal, nämlich bei Start, ausgeführt.
Christian
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Do 22.09.05 11:40
Überschrieben wird nichts, der alte Inhalt bleibt als Leiche dort. Erst wenn der Speicher neu zugewiesen wird, ändert sich der Inhalt ggf. Deshalb ist das nicht sofort zu erkennen, wenn man mit Zeigern arbeitet und debuggt.
Wenn p dein Zeiger auf den Beginn des Speicherblockes ist und q der auf den Ende dieses Blocks dann ist nach deiner Logik q - p die Länge des Speicherblocks. Wenn nun ein größerer Speicher benötigt wird kann es passieren, dass ab der Stelle, auf die p zeigt, nicht mehr genug Platz ist, weil nach dem Speicherblock schon andere genutzte Speicherblöcke liegen. Dann wird der neue Block an anderer Stelle zugewiesen. Aber dass heißt dann doch, dass p nicht (mehr) auf den Anfang des (neuen) Speicherblocks zeigt. Wenn der neue Block vor dem alten liegt, wäre q - p negativ und wenn er danach liegt zu groß!
Wenn du zuerst SetSize aufrufst und dann p zuweist passt das. Wenn du erst p zuweist und dann SetSize nicht. Denke ich da richtig?
|
|
sahib
      
Beiträge: 117
Win 2000 SP4+
Delphi 5 Prof.
|
Verfasst: Do 22.09.05 11:48
Danke, danke!
Stimmt, ich habe nicht bedacht, dass der Speicher nicht aneinander liegen muss. Das müsste es sein. Kann ich ja später testen.
*EDIT* Ok, wo ist der Knopf 'Als gelöst' hin? Ich habe hier soetwas nicht und die Buttons 'Style', 'Bereiche' etc funktionieren auch nicht.
Christian
(wieder mit altem Pseudonym)
|
|
|