![]() |
TStringList und Add-/InsertObject (Verständnisprobleme)
Verständnisprobleme
------------------- Ähnlich wie in 'TStringList-Objekte auslesen' möchte ich zum speichern von Daten eine StringList benutzen.
Delphi-Quellcode:
Bisher war TDataSet als record definiert und beim schreiben in die String-List musste jedesmal die Startposition berechnet werden.
type
TDataSet = class(TObject) public Data1 : String; Data2 : String; u.s.w. end; var DataSetList : TStringList; In der Hoffnung dieses verhindern zu können versuche ich die Datenstruktur auf 'Object' umzustellen (siehe oben) und die InsertObject und AddObject Eigenschaften der String-List zunutzen. Daten in string-list speichern: -------------------------------
Delphi-Quellcode:
Ich habe mir während des speicherns in die String Liste die Daten angesehen die gespeichert wurden und musste feststellen daß eigentlich nur der '<text>' in der liste auftauchte.
procedure DataSetSpeichern;
var Position : Integer; TempDataSet: TDataSet; begin TempDataSet := TDataSet.Create; <...Daten von Eingabe-Form lesen, Rückgabe ist TempDataSet...> <...Position bestimmen...> DataSetList.InsertObject(Position, '<text>', TempDataSet); TempDataSet.Free; end; Frage: Was wird hier eigendlich gespeichert und wo ist der Rest der Daten die sich in TempDataSet (TDataSet) befanden ? Daten aus string-list lesen: ----------------------------
Delphi-Quellcode:
Beim auslesen der Daten klappt es gar nicht. Die procedure die 'DataSetLesen' aufruft beinhaltet natürlich auch 'TempDataSet := TDataSet.Create;' und 'TempDataSet.Free;' wird erst dann aufgerufen
function DataSetLesen(Position: Integer): TDataSet;
begin TempDataSet := DataSetList.Objects[Position] as TDataSet; end; wenn die Daten komplett abgearbeitet sind. Kann aber genau das mein Problem sein ('TempDataSet.Free;') ? Meine Vermutung ist daß möglicherweise nur ein Pointer in der String List gespeichert wird und ich beim aufrufen des '.Free' statements das ziel des Pointers vernichte. Desweiteren würde ich mich über Informationen über Speichern und Lesen solcher daten in files freuen. Danke und schönes Wochenende, Karsten [edit=Daniel B]Delphi-Tags korrigiert. Mfg, Daniel B[/edit] |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Zitat:
Tipp für den Richtigen weg: Erweitere TDataSet (dieser Name ist unglücklich da es schon eine Delphi-Klasse mit diesen Namen gibt) um ein Feld Caption und verwende statt TStringList TObjectList (bzw. einen Nachfahren davon). Diese hält das Object und verwaltet es. Trotzdem diese erste Zeile meines Positings lassen. Das speichern kann man dann auch ohne weiteres impl.. Falls du die Caption als Mapper verwendest so kann man in dem abgeleiteten Object eine solche Funktion implementieren. Schreib mal was du genau willst, dann sehen wir weiter! |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Mein ziel ist das Verwalten (lesen von files, speichern in files, hinzufügenvon Daten, entfernen von Daten und lesen von Daten) von Daten vom Typ wie ich sie als (dummerweise) TDataSet definiert habe; und das
in unbestimmter Menge. Die StringList habe ich nur gewählt weil man relativ einfach die Daten aus einem File einlesen kann und genauso speichern. ...LoadFromFile ...SaveToFile Das war zu dem Zeitpunkt wo ich mit angefangen habe mit Delphi zu arbeiten. Irgendwann habe ich festgestellt das eine StringList auch die Möglichkeit von ...InsertObject bietet und wollt diese ausprobieren um mir die ständige Bestimmung der Position (wo fängt der Datensatz an ?) zu ersparen. Leiser fehlten mir wesentliche Informationen über die Nutzung von StringListen mit Objecten. Leider kann ich über TObjectList in meinem Delphi Buch auch nichts finden, werde es aber ausprobieren. Ansonsten ist mal ein neues Buch angesagt. Danke, Karsten |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Ich mach das wiefolgt...
Delphi-Quellcode:
Das ist aber schon tief im Nähkästchen
TBlabla = class
public constructor Create; // erzeugt neues Object constructor Load(stm : TStream); virtual; // lädt object aus Datenstrom procedure Store(stm : TStream); virtual; // speichert object in D-Strom end; TBlaBlaList = class public constructor LoadFromFile(asFileName : String); virtual; constructor LoadFromStream(stm: TStream); virtual; procedure SaveToFile(asFileName : String); virtual; procedure SaveToStream(stm: TStream); virtual; end; |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Ich denke daß ich schon einen Schritt weiter bin, aber mit dem Speichern / Lesen in / aus eine(r) Datei bin ich kläglich :cry: gescheitert.
Delphi-Quellcode:
Die folgenden Proceduren habe ich aus einem Handbuch kopiert und auf meie Bedürfnisse angepasst. Angeblich soll das funktionieren, wobei man im Original Arrays mit der Variante gespeichert hat. Die Tatsache das die Daten nicht gespeichert werden lässt mich zum Schluß kommen, daß sie (die Proceduren) absolut fehlerhaft sind oder sich noch prinzipielle Fehler im Aufsetzen der Daten bzw. in der Handhabung befinden :?: . Habe mich vorher noch nie mit Streams befasst.
type
TDaten = class(TObject) public Daten1 : string; Daten2 : string; Daten3 : string; end; type TDatenListe = class(TObjectList) public procedure WriteToFile(DatenListe: TDatenListe; FileName: String); procedure ReadFromFile(DatenListe: TDatenListe; FileName: String); end; var DatenListe : TDatenListe;
Delphi-Quellcode:
procedure TDatenListe.WriteToFile(DatenListe: TDatenListe; FileName: String);
var DatenStream: TFileStream; begin if FileExists(FileName) then begin; try DatenStream := TFileStream.Create(FileName, fmOpenWrite); DatenStream.WriteBuffer(DatenListe, SizeOf(TDatenListe)); DatenStream.Free; except MessageDlg('Fehler beim Schreiben der Daten', mtError, [mbOK], 0); end; end else begin; try DatenStream := TFileStream.Create(FileName, fmCreate); DatenStream.WriteBuffer(DatenListe, SizeOf(TDatenListe)); except MessageDlg('Fehler beim Schreiben der Daten', mtError, [mbOK], 0); end; DatenStream.Free; end; end;
Delphi-Quellcode:
Schreiben:
procedure TDatenListe.ReadFromFile(DatenListe: TDatenListe; FileName: String);
var DatenStream: TFileStream; begin try DatenStream := TFileStream.Create(FileName, fmOpenRead); DatenStream.Position := 0; DatenStream.ReadBuffer(DatenListe, SizeOf(TDatenListe)); except MessageDlg('Fehler beim Lesen der Daten', mtError, [mbOK], 0); end; DatenStream.Free; end;
Delphi-Quellcode:
Löschen:
DatenListe.Insert(<Position>, Daten)
Delphi-Quellcode:
Ändern:
DatenListe.Delete(<Position>)
Delphi-Quellcode:
Lesen:
DatenListe.Delete(<Position>)
DatenListe.Insert(<Position>, Daten)
Delphi-Quellcode:
[edit=Daniel B]Delphi-Tags korrigiert. Mfg, Daniel B[/edit]
??? Element aus DatenListe an <Position> in Daten einlesen
|
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Moin Karsten,
Du versuchst ein Objekt vom Typ TDatenliste zu speichern, also inklusive aller Methoden, also nicht nur die Daten. Das heisst, Du müsstest zum Speichern die Daten alleine bereitstellen. SizeOf(TDatenListe) gibt übrigens vier zurück, da TDatenListe ein Objekt ist, und somit die Grösse die eines Pointers ist. Warum TDaten bei Dir ein Objekt ist, und kein Record bzw. packed record, kann ich im Moment nicht nachvollziehen. Dann könntest Du eine Variable vom Typ TDaten Schreiben, mit SizeOf(TDaten). Dass könnte dann grundsätzlich funktionieren, wobei der Typ String auch wieder ein Problem darstellt, da strings dynamische Arrays, und somit auch wieder Pointer sind. Die von Dir genannte Struktur wäre also, als packed record, 12 Byte gross. Strings müsstest Du dann also entweder als ShortStrings einbauen (mit Längenangabe, aber nur maximal 255 Stellen), oder mittels einer Routine schreiben, die den Inhalt ausliest, und die Längen berücksichtigt. Das Schreiben könnte z.B. so aussehen: (hier nur die Änderungen, create usw. fehlt)
Delphi-Quellcode:
Die Längen solltest Du auf jeden Fall mit speichern (müssen allerdings nicht in TDaten enthalten sein), damit Du auch bei leeren Strings weisst was zu tun ist.
type
TDaten = packed record dwLen1 : DWORD; sValue : string; //... end; var tdWork : TDaten; begin // erst einmal die Länge merken tdWork.dwLen1 := length(tdWork.sValue); // Vier Byte schreiben, da ein DWORD 4 Byte gross ist DatenStream.Write(tdWork.dwLen1,4); // Nur den String schreiben, wenn er etwas enthält if tdWork.dwLen1 > 0 then begin // Den String ab der ersten Stelle schreiben // Ohne [1] funktioniert es nicht, da dieser Parameter nicht typisiert ist DatenStream.Write(tdWork.sValue[1],tdWork.dwLen1); end; end; Wichtig: Beim Auslesen muss der auszulesende String vor dem Auslesen schon auf die richtige Länge bebracht werden, z.B. mit SetLength. Was übrigens noch fehlt, ist der Resourcenschutzblock try/finally. Damit müsste sichergestellt werden, dass das Dateihandle auch wieder freigegeben wird. Beispiel:
Delphi-Quellcode:
// Resource belegen
DatenStream := TFileStream.Create(....); try // Mach was mit DatenStream finally // Resource freigeben FreeAndNil(DatenStream); end; |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Hallo,
also sind wir wieder beim record. Wobei, was bedeutet 'packed record' ? Wenn ich mich bei den 'strings' auf eine Länge festlegen würde, kann man dann auf die Längenangabe als Datenwort (dwLen1 : DWORD;) verzichten ? Was könnte man dann als Container-Komponente benutzen, die eine eine unbestimmte Anzahl an records beinhaltet. Ziel ist es die Daten einmal komplett aus aus einer Datei auszulesen, zu bearbeiten und bei bedarf wieder zu speichern. Wichtige Kriterien sind hier ein relatiev einfaches Einfügen (am Anfang, mitten drin und am Ende), Löschen und Ausleben von Daten des Typs TDaten. Eine Idee meinerseits währe hier ein dynamisches Array vom Typ TDaten, wobei (soweit mein Wissensstand) man hier nach dem Ändern der Größe eines Arrays erst sämtliche Daten verschieben muß um einen neuen Datensatz in die Datenstruktur (nicht gerade am Ende) einzufügen. Jetzt warte ich nur noch auf den Kommentar das es sich hier wieder auch nur um eine Pointerliste handelt ( :angle2: ) die man nicht ohne weiteres in eine Datei speichern kann. Falls doch nicht, welch Speichermethode währe hier zu wählen ? Da sich sämtliche Daten in der Container-Komponente befinden müsste eine bereits existierende Datei komplett überschrieben werden. Gruß, Karsten |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
@Karsten
Mit Objekten kann man wirklich alles lösen, niemand braucht sich mit Arrays ärgern. Ich hab keine Ahnung was genau du machen willst, aber ich will dir mal eine Möglichkeit zeigen, ob du es nutzen kannst ist was anderes. Du kannst dir eine Stringlist nehmen die Stringlisten verwaltet :mrgreen: Wenn du Spass dran hast kannst du auch eine Stringlist nehmen die wiederum Stinglisten verwaltet und auch die zweite Ebene Stringlisten könnten Stinglisten aufnehmen. Dies könnte man ohne ende so weiter treiben, alles möglich könnte aber jetzt zu weit führen :firejump: Die Stringlisten der zweiten Ebene könnte man alle extra in einer Datei speichern oder du machst eine große davon (könnte sich dann wohl sparen, aber machen könnte man es ohne weiteres). |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Ich habe es auch vorher mit einer Stringliste realisiert. Wobei es hier wirklich nur eine Stringliste war und man beim ein- auslesen von
Daten immer die Startposition berechnen musste. Der Grund mich für eine Stringliste zu entscheiden war das einfache speichern und lesen von Daten in einer Datei.
Code:
Nur beim Auslesen oder Einfügen eines einzelnen Datensatzes muß man jedesmal die Startposition berechnen.
DatenListe.LoadFromFile(DateiName);
DatenListe.SaveToFile(DateiName); Desweiteren ist man hier auf den Datentypen 'String' beschränkt. Im allgemeinen kann man mein Vorhaben mit einer Adress-Verwaltung vergleichen, wobei ich mir erst einmal Gedanken über die Datenstruktur mache um später nicht extrem komplexe Verfahren anwenden muß um irgendwelche Funktionen zu realisieren. Am Anfang sind die Anforderungen wie folgt: simples - auslesen (aller Daten) aus einer Datei - schreiben (aller Daten) in eine Datei - einfügen von Daten in die Datenstruktur - löschen von Daten aus der Datenstruktur - ändern von existierenden Daten in der Datenstruktur was hoffentlich auch für die zukunft ausreicht. Es geht hier lediglich um die Verwaltung von Daten eines einzelnen Typs. Bei der Datei handelt es sich um eine Typisierte Datei da die Daten immer vom Typ TDaten sind aber nicht umbedingt nur strings sein müssen. Gruß, Karsten |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Dann benutz doch eine Klasse statt eines Records, und pack die ganze mechanik da hinein.
Um eine TList baust du eine Wrapper-Klasse, welche deine Daten verwaltet. Ist ein bisschen mehr Schreibarbeit, aber wesentlich besser wartbar und in ein paar Wochen noch nachvollziehbar....
Delphi-Quellcode:
packed record= entfernt die padding(Füll-) bytes welche Delphi per default einfügt.unit Unit1; interface uses classes,SysUtils; type TDaten=class constructor create (s:string;i:integer); private mytext:String; myinteger:integer; public procedure readFromStream(aStream:tstream); function writeToStream(aStream:tstream):integer; end; TDatenContainer=class constructor create(fname:string);overload; destructor destroy;override; private filename:string; mylist:tlist; public procedure addItem(td:tdaten); function getItemAt(i:integer):tdaten; procedure readFromStream(aStream:Tstream); procedure writeToStream(aStream:TStream); end; implementation constructor TDaten.create(s:string;i:integer); begin mytext:=s; myinteger:=i; end; procedure tDaten.readFromStream(aStream:Tstream); var len:integer; begin aStream.read(myinteger,sizeof(myinteger)); aStream.read(len,sizeof(len)); setLength(mytext,len); aStream.read(mytext[1],sizeof(mytext)); //... end; function TDaten.writeToStream(aStream:tstream):integer; var len:integer; begin result:=aStream.position; aStream.write(myinteger,sizeof(myinteger)); len:=length(mytext); aStream.write(len,sizeof(len)); aStream.write(mytext[1],sizeof(mytext)); //... end; constructor TDatenContainer.create(fname:string); begin inherited create; filename:=fname; mylist:=tlist.create; end; procedure TDatenContainer.addItem(td:tdaten); begin mylist.add(td); end; function TDatenContainer.getItemAt(i:integer):tdaten; begin result:=nil; if i<mylist.count then result:=mylist.items[i]; end; procedure TDatenContainer.readFromStream; var mystream:tfileStream; mydaten:tdaten; i:integer; begin try mystream:=tfilestream.create(filename,fmopenread); mystream.read(i,sizeof(i)); for i:=0 to mylist.count-1 do begin mydaten:=TDaten.create; //erst Instanz erzeugen, sorry! mydaten.readFromStream(mystream); Mylist.add(mydaten); end; finally mystream.free; end; end; procedure TDatenContainer.writeToStream; var mystream:tfileStream; mydaten:tdaten; i,len:integer; begin try mystream:=tfilestream.create(filename,fmopenread); len:=mylist.count; mystream.write(len,sizeof(len)); for i:=0 to mylist.count-1 do begin mydaten:=mylist.items[i]; mydaten.writeToStream(mystream); end; finally mystream.free; end; end; destructor TDatenContainer.destroy; begin mylist.free; inherited; end; end. cu Waba |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Danke, das ist soweit genau das was ich wollte. Vor allem leicht wartbar wenn es bei mir dann auch funktioniert.
Frage: In 'TDatenContainer.ReadFromStream' und 'TDatenContainer.WriteToStream' benutzt Du 'MyDaten:TDaten' machst aber kein 'Create', funktioniert das auch so ? Diese Frage entstand durch folgendes Problem: 1. Ein Datensatz wurde bereits vorher gespeichert. 2. TempDaten wurde in die WatchList aufgenomen und die procedure 'LadenClick' (siehe unten) aufgerufen. 3. Nachdem TempDaten angelegt wurde 'TempDaten:=TDaten.Create('', 0)', wird der erste Datensatz (0) ausgelesen. Problem: Nach ausführen von 'getItemAt(0)' ist TempDaten anscheinend nicht mehr vorhanden (Inaccessible value). Das führt natürlich zu einem Fehler wenn ich die Daten verarbeiten will.
Delphi-Quellcode:
Hat das alles wieder etwas mit 'Pointern' zu tun ?
procedure TMainForm.LadenClick(Sender: TObject);
var FileName: String; TempDaten: TDaten; begin FileName := InputBox('Laden', 'File Name:', '<datei>'); if FileExists(FileName) then begin DatenListe := TDatenContainer.Create(FileName); DatenListe.ReadFromFile; TempDaten := TDaten.Create('', 0); TempDaten := DatenListe.getItemAt(0); if TempDaten = nil then begin exit; end; Label1.Caption := TempDaten.mytext; Label2.Caption := IntToStr(TempDaten.myinteger); TempDaten.Free; end; end; Gruß, Karsten [edit=Daniel B]Delphi-Tags korrigiert. Mfg, Daniel B[/edit] |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Sorry, hatte vergessen die Instanz zu erzeugen, ist editiert.
Objectreferenzen sind Zeiger, in der Liste befinden sich deshalb auch nur Zeiger. cu waba |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
O.k., jetzt goenne einem Neuling im Bereich OOP noch ein/zwei Fragen.
Wenn MyList nur Pointer enthaelt muss also fuer jeden neuen Datensatz (MyDaten) eine neue Instanz erzeugt werden und der Pointer auf diese Instanz wird in MyList abgelegt. Die in 'ReadFromStream' erzeugten Inztanzen von MyDaten bleiben somit erhalten. Wie ist das dann wenn ich einen Datensatz entfernen will ? Im Prinziep muesste ich mir ja erst den Pointer aus MyList auslesen, die damit adressierte Instanz von MyDaten freigeben und dann den Pointer aus MyList entfernen, oder ? Das gleiche gilt natuerlich auch fuer den Fall dass ich die gelesene / geschriebene Datei wieder schliesse bzw. die DatenListe vom Typ TDatenContainer, richtig ? Aber wie ist das zu erklaeren dass TempDaten nach getItemAt anscheinend nicht mehr existent ist obwohl diese Instanz vorher angelegt wurde (Inaccessible value) ? Ich denke dass hier noch ein generelles Verstaendnisproblem meinerseits besteht. Mit dem Aufruf readItemAt lese ich doch einen Pointer auf die gewuensche Instanz aus und die Zeile 'TempDaten := DatenListe.getItemAt(0);' sollte mir doch einen Pointer auf die erste Daten Instanz zurueck liefern oder liege ich hier total daneben ? Wobei ist das ueberhaupt so sinnvoll was ich da mache ? Eine Instanz zu erzeugen, dieser einen neuen pointer zuweisen und sie am Ende wieder freigeben. Vor allem stellt sich hier die Frage was freigegeben wird. Ich habe bisher alles verstanden was Du geschrieben hast, nur bei meinen Sachen kommen immer groesserer Zweifel auf. Gruss, Karsten |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Delphi-Quellcode:
TempDaten und DatenListe.getItemAt(0) zeigen auf den selben Speicherbereich, machst du auf einen von beiden ein free, zeigen beide Pointer auf einen ungültigen Speicherbereich.TempDaten := DatenListe.getItemAt(0); try ... finally TempDaten; // macht beide free end; |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
cu waba |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Es funktioniert :hello: !!!
Mein erstes Problem war das ich vor dem Auslesen der Daten erst einmal eine Instanz von TDaten erzeugt habe (TempDaten ... Create), was so schwachsinnig war wie der Farbe beim Trocknen zuzusehen. Die Instanz von TDaten war ja bereits vorhanden und ich musste lediglich nur noch den Pointer über getItemAt lesen. Dann hatte ich noch ein weiteres Problem mit einem Fehler beim Schreiben bzw. Lesen der Daten in bzw. aus der Datei. aStream.read(mytext[1],sizeof(mytext)); aStream.write(mytext[1],sizeof(mytext)); Wie ich hier gelernt habe gibt SizeOf(<String>) die Länge des Pointers zurück weil 'String' auch nur ein Pointer ist. Christian Seehase: ...wobei der Typ String auch wieder ein Problem darstellt, da strings dynamische Arrays, und somit auch wieder Pointer sind. Ich denke das Du 'aogwaba' die Fehler absichtlich eingebaut hast um den Lerneffekt zu testen :angle2: . Der war auf jeden Fall sehr hoch. Ein großes DANKE an alle die, die mir bei meinen ersten Schritten in die OOP Welt geholfen haben. Ich denke daß ich jetzt in der Lage bin fehlende Komponenten eigenständig zu erstellen. Weiterhin frohes Programmieren :coder: , Karsten |
Re: TStringList und Add-/InsertObject (Verständnisprobleme)
Mit Absicht war das nicht... :wink:
cu waba |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:52 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