Announcement

Collapse
No announcement yet.

TList laden und speichern

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • TList laden und speichern

    Hallo,<P> ich habe eine TList (in der ich Records hineinspeichere) programmiert. Alles klappt so weit nur das Speichern und das Laden nicht. Egal was ich versuche es kommen entwender Fehlermeldungen oder nur Schei... raus. Auch im Forum (Suchen nach TList) hat mich nicht weitergebracht. Also hier meine Frage wie kann ich die TList in eine Datei speichern und wieder laden. Ein paar Programmzeilen wären nett. Danke.

  • #2
    Hallo,

    TList speichert ein Array von Zeigern - also nur 32-Bit-Adresswerte auf einen Datenblock. Die Wahrscheinlichkeit, dass nach dem erneuten Starten der Anwendung (neuer Prozess mit nagelneuem 4 GByte virtuellen Adressraum) die "alten" Zeigerwerte noch zu irgend etwas sinnvollem zu verwenden sind, ist gleich 0.

    Anstelle die Adresszeiger zu speichern, müssen die Objekte selbst mit der Fähigkeit nachgerüstet werden, sich persistent speichern zu können. Der Mechanismus wird in der Delphi-Hilfe unter <b>TReader</b> und <b>TWriter</b> beschrieben.

    Wenn es nur um die Speicherung von Records mit fester Struktur geht, stellt sich die Frage, warum das Rad ständig neu erfunden werden muss. Sowohl mit <b>TClientDataSet</b> als auch mit dem <b>RecordSet</b>-Objekt von ADO stehen sofort einsatzfertige, sehr leistungsfähige und flexible Objekte zur Verfügung (Datenspeicherung in einer Datei). Während TClientDataSet nur in der Enterprise-Version zur Verfügung steht, kann jede Version von Delphi 4/5 direkt auf die COM-Objekt von ADO zugreifen (ADO Express wird also nicht unbedingt benötigt).
    &#10

    Comment


    • #3
      Hallo Andreas,<P> erst einmal Danke für die Erklärung, denn da hab ich wohl was falsch verstanden.<br>Mein Problem ist, eine kleine Datenbank zu programmieren (zwei Eingabefelder). Mir stehen aber in der Delphi 5.0 Std Version keine Tools zur Verfügung (weder TClientDataSet noch ADO).<br>Ich habe in der Hilfe unter TWriter und TReader nachgeschaut, aber so richtig schlau bin ich nicht daraus geworden.<BR>Und eins noch zum Schluß was hat der letzte Satz zu bedeuten (..."direkt auf die COM-Objekt von ADO zugreifen"...), heißt daß, ich kann auch ohne das mir in Delphi ADO angeboten wird, über COM-Objekte zugreifen? Wenn ja wie?<BR>Also kurz gesagt, wie kann ich das oben beschrieben Problem für mich am einfachsten lösen (mit meinen beschränkten Mitteln)

      Comment


      • #4
        Hallo,<br>
        wenn es nur darum geht zwei Felder eines Records zu speichern würde ich einfach eine typisierte Datei verwenden, schließlich werden die Records durch TList doch nur verkettet.<br&gt

        Comment


        • #5
          Hallo,<P>momentan lautet mein Source so:<P>
          Ich ermittle die in TList abgespeicherten Datensätze, schreibe diese Anzahl in einen Stream um anschließend in einer while-Schleife die Records in den Stream zu schreiben.<P>Das ist nicht gerade ein elegante Möglichkeit, aber die einzige die mir im Moment als möglich erscheint. Ein weiters Problem ist, dass das Record sehr speicherfressend ist (Name: string[30]), d. h. egal wie lange der Name ist, es werden im 30 Byte in den Stream geschrieben.<P>Also das Problem nochmal, wie kann ich mit Delphi 5.0 Standart eine kleine Datenbank entwickeln

          Comment


          • #6
            Hallo,

            man muss nicht unbedingt die <b>ADO Express</b>-Komponenten besitzten, um ADO einsetzen zu können. Das folgende Beispiel demonstriert, wie der Inhalt einer dBASE-Tabelle in einem <b>TStringGrid</b> (kein TDBGrid) angezeigt wird:
            <pre>
            uses ComObj;

            const
            cSELECT = 'SELECT * FROM clients.dbf';
            cDSN = 'ADODBDEMOSdBASE';

            procedure TFormMain.ToolButtonOpenClick(Sender: TObject);
            var
            vRecordSet : OleVariant;
            iRow, iCol : Integer;
            begin
            vRecordSet := CreateOleObject('ADODB.Recordset');
            vRecordSet.Open(cSELECT, cDSN);
            iRow := 1;
            repeat
            for iCol := 0 to 6 do
            StringGrid1.Cells[iCol, iRow] := vRecordSet.Fields[iCol].Value;
            vRecordSet.Move(1);
            Inc(iRow);
            until vRecordSet.EOF;
            end;
            </pre>
            Anstelle des ODBC-DSN <i>ADODBDEMOSdBASE</i> (Systemsteuerung | ODBC32-Icon | neuen ODBC-DSN für die eigene Datenbank anlegen) könnte man auch einen anderen ConnectionString für eine andere Datenbank verwenden. Zum Beispiel erlaubt die Zeichenkette "<i>Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Database\dbdemos.mdb;Persist Security Info=False</i>" den Zugriff auf die Delphi-Beispieldatenbank <i>dbdemos.mdb</i>.

            Neben dem Auslesen von Daten ist auch das Updaten/Einfügen von Daten möglich. Das folgende Beispiel demonstriert, wie ein Wert geändert werden kann. Im Gegensatz zum ersten Beispiel greife ich hier auf die <i>frühe Bindung</i> (_Recordset-Interface) zurück, damit die Programmier-Hilfe von Delphi 5 auch für die ADO-Objekte von Microsoft zur Verfügung steht. Dazu muss über <i>Projekt | Typbibliothek importieren</i> die ADO-Typbibliothek einmalig in Delphi importiert und die von Delphi automatisch generierte <b>*_TLB.pas</b>-Datei in die eigene Uses-Aufzählung aufgenommen werden - danach kennt Delphi alle ADO-Objekte:
            <pre>
            procedure TFormMain.ToolButton2Click(Sender: TObject);
            var
            aRecordSet : _Recordset;
            begin
            aRecordSet := CoRecordset.Create;
            aRecordSet.Open(cSELECT, cDSN, adOpenForwardOnly, adLockOptimistic, adOpenForwardOnly);
            aRecordSet.Move(StringGrid1.Row - 1, EmptyParam);
            aRecordSet.Fields[StringGrid1.Col].Value := EditNewValue.Text;
            aRecordSet.Update(EmptyParam, EmptyParam);
            end;

            procedure TFormMain.StringGrid1SelectCell(Sender: TObject; ACol,
            ARow: Integer; var CanSelect: Boolean);
            begin
            EditNewValue.Text := StringGrid1.Cells[ACOl, ARow];
            end;
            </pre>

            Es muss dabei nicht unbedingt eine dBASE- bzw. ACCESS-Datenbank sein (auch wenn diese problemlos neu angelegt/bearbeitet werden können). Mann kann auch direkt eine Datendatei bzw. eine XML-Datei als Speicherort verwenden.

            Das <b>MDAC-SDK</b> inklusiver der Microsoft-Dokumentation der ADO-Objekte kann von der Microsoft-Webseite <i>http://www.microsoft.com/data/download.htm#26info</i> heruntergeladen werden

            Comment


            • #7
              Hallo Andreas,<P> danke für die umfangreiche Antwort. Das hilft mir sehr viel weiter. Ich werds mir nächste Woche mal genauer ansehen. Danke, Danke, Danke

              Comment


              • #8
                Hallo Andreas,<P> ich habe mich am Wochenende ADO durchgearbeitet. Funktioniert sehr schön. Dachte, dass so was mit Delphi 5 Std nicht möglich ist - aber der Mensch irrt. Viel Fragen konnte ich im Ordner ADO bereits finden. Nur eine Frage habe ich noch. Egal was ich mache, eine neuen ODBC-DSN anlegen funktioniert nicht (oder ich stell mich nur doof an). Kannst du ggf. die einzelnen Schritte nochmals erklären (Systemsteuerung | ODBC32-Icon ...).<P> Danke

                Comment


                • #9
                  Hallo,

                  ein ODBC-DSN ist nur ein Weg von vielen, aber nicht immer der Eleganteste. Alternativ dazu kann direkt ein <i>ADO-ConnectionString</i> verwendet werden, zumal dafür sogar ein Konfigurationsdialog auf jedem Rechner aufgerufen werden kann:

                  <b>a) Direkter Weg</b>

                  1. Windows-Explorer: Über <i>Datei | Neu</i> neue Textdatei mit dem Dateinamen <i>Test.txt</i> anlegen. <br>
                  2. Windows-Explorer: Die Datei nach <i>Test.udl</i> umbenennen. <br>
                  3. Windows-Explorer: Doppelklick auf <i>Test.udl</i> - der Microsoft-Konfigurations-Dialog <b>Datenverknüpfungseigenschafen</b> wird angezeigt. Hier kann man sich auf den verschiedenen Registerseiten die Verbindungselemente zusammensuchen und die Verbindung testen. <br>
                  4. War das erfolgreich, die UDL-Datei als Textdatei betrachten:
                  <pre>
                  [oledb]
                  ; Everything after this line is an OLE DB initstring
                  Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Database\dbdemos.mdb;Persist Security Info=False
                  </pre>
                  5. Über die Zwischenablage diesen ConnectionString kopieren und im eignen Programm verwenden.

                  <b>b) ODBC-DSN</b>

                  - Systemsteuerung | Datenquellen (ODBC) <br>
                  - Benutzer-DSN | Hinzufügen | Treiber auswählen<br>
                  - Datenquellennamen frei wählen <br>
                  - Datenbank über Datei öffnen-Dialog auswähle

                  Comment


                  • #10
                    Hallo,<br>damit ich eine Liste speichern kann, verwende ich TCollection und TCollectionItem anstatt TList. <br> Unit2 zeigt wie es geht:<br>
                    <pre><font size="1" face="Verdana">
                    unit Unit2;

                    interface

                    Uses
                    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
                    StdCtrls, ComObj,ActiveX;

                    Type
                    TWvgObject = Class(TPersistent)
                    private
                    FX : Integer;
                    FStr : String;
                    public
                    procedure Assign(Source : TPersistent); override;
                    published
                    property StrText : String read FStr write FStr;
                    property X : Integer read FX write FX;
                    end;

                    TWvgItem = class(TCollectionItem)
                    private
                    FDate : TDateTime;
                    FText : String;
                    FWvgObject : TWvgObject;
                    public
                    constructor Create(Collection: TCollection); override;
                    destructor Destroy; override;
                    procedure Assign(Source : TPersistent); override;
                    published
                    property Date : TDateTime read FDate write FDate;
                    property Text : String read FText write FText;
                    property WvgObject : TWvgObject read FWvgObject write FWvgObject;
                    end;

                    TWvgItems = class(TCollection)
                    private
                    function GetItem(Index: Integer): TWvgItem;
                    procedure SetItem(Index: Integer; const Value: TWvgItem);
                    public
                    constructor Create;
                    function Add : TWvgItem;
                    property Items[Index : Integer] : TWvgItem read GetItem write SetItem;
                    end;

                    TStreamDummy = class(TComponent)
                    private
                    FItems : TWvgItems;
                    public
                    constructor Create(AOwner : TComponent); override;
                    destructor Destroy; override;
                    published
                    property Items : TWvgItems read FItems write FItems;
                    end;

                    implementation

                    { TWvgItem }

                    procedure TWvgItem.Assign(Source: TPersistent);
                    begin
                    If Source is TWvgItem then
                    begin
                    FDate:=TWvgItem(Source).Date;
                    FText:=TWvgItem(Source).Text;
                    FWvgObject.Assign(TWvgItem(Source).WvgObject);
                    end
                    else
                    inherited Assign(Source);
                    end;

                    constructor TWvgItem.Create(Collection: TCollection);
                    begin
                    inherited Create(Collection);
                    FWvgObject:=TWvgObject.Create;
                    end;

                    destructor TWvgItem.Destroy;
                    begin
                    FWvgObject.Free;
                    inherited;
                    end;

                    { TWvgItems }

                    function TWvgItems.Add: TWvgItem;
                    begin
                    Result:=inherited Add as TWvgItem;
                    end;

                    constructor TWvgItems.Create;
                    begin
                    inherited Create(TWvgItem);
                    end;

                    function TWvgItems.GetItem(Index: Integer): TWvgItem;
                    begin
                    Result:=inherited GetItem(Index) as TWvgItem;
                    end;

                    procedure TWvgItems.SetItem(Index: Integer; const Value: TWvgItem);
                    begin
                    inherited SetItem(Index,Value);
                    end;

                    { TStreamDummy }

                    constructor TStreamDummy.Create(AOwner: TComponent);
                    begin
                    inherited Create(AOwner);
                    FItems:=TWvgItems.Create;
                    end;

                    destructor TStreamDummy.Destroy;
                    begin
                    FItems.Free;
                    inherited Destroy;
                    end;

                    { TWvgObject }

                    procedure TWvgObject.Assign(Source: TPersistent);
                    begin
                    If Source is TWvgObject then
                    begin
                    FStr:=TWvgObject(Source).StrText;
                    FX:=TWvgObject(Source).X;
                    end
                    else
                    inherited Assign(Source);
                    end;

                    end.
                    </font></pre><br>TWvgObject soll zeigen, das einem TCollectionItem noch ein Object anhängen kann. TWvgObject wird automatisch gespeichert

                    Comment


                    • #11
                      Bei Klasse TStreamDummy handelt es sich lediglich um eine Hilfsklasse.
                      Dadurch, das Items in TStreamDummy published ist, wird die Collection beim schreiben in einen Stream mit geschrieben. Für das Speichern und Laden aus einem Stream verwende ich das IStorage Interface.<br>
                      <pre><font size="1" face="Verdana">
                      function LoadAnalyseFromFile(aAnalyse : TTPAnalyse; const Filename : String) : TTPAnalyse;
                      var
                      aFilename : WideString;
                      FRootStorage : IStorage;
                      stm : IStream;
                      OS : TOleStream;
                      StreamDummy : TTPAnalyseDummy;
                      begin
                      Result:=aAnalyse;
                      If Result=Nil then
                      Result:=TTPAnalyse.Create;
                      aFilename:=Filename;
                      If Not Succeeded(StgOpenStorage(PWideChar(aFilename),
                      nil,
                      STGM_READWRITE or
                      STGM_SHARE_EXCLUSIVE,
                      nil,
                      0,
                      FRootStorage)) then
                      raise EFilenameError.Create('Konnte '+Filename+' nicht öffnen');
                      OleCheck(FRootStorage.OpenStream(ANALYSEDATA,nil,
                      STGM_READ or
                      STGM_SHARE_EXCLUSIVE,
                      0,stm));
                      Result.Clear;
                      StreamDummy:=TTPAnalyseDummy.Create(Nil);
                      Try
                      OS:=TOleStream.Create(stm);
                      Try
                      OS.ReadComponent(StreamDummy);
                      Result.Assign(StreamDummy);
                      Finally
                      OS.Free;
                      end;
                      Finally
                      StreamDummy.Free;
                      end;
                      end;</font></pre>
                      <br>Die function LoadAnalyseFromFile zeigt, wie man eine Collection aus dem Stream wieder lädt. Ok. die Klassennamen sind etwas unterschiedlich. Aber ich hoffe, dass stört nicht.<br>
                      <pre><font size="1" face="Verdana">
                      procedure SaveAnalyseToFile(aAnalyse : TTPAnalyse; const Filename : String);
                      var
                      FRootStorage : IStorage;
                      aFilename : WideString;
                      stm : IStream;
                      OS : TOleStream;
                      StreamDummy : TTPAnalyseDummy;
                      begin
                      aFilename:=Filename; // Für Typecasting
                      OleCheck(StgCreateDocFile(PWideChar(aFilename),
                      STGM_CREATE or
                      STGM_READWRITE or
                      STGM_SHARE_EXCLUSIVE,
                      0,
                      FRootStorage));

                      OleCheck(FRootStorage.CreateStream(ANALYSEDATA,
                      STGM_CREATE or
                      STGM_WRITE or
                      STGM_SHARE_EXCLUSIVE,
                      0,0,stm));
                      StreamDummy:=TTPAnalyseDummy.Create(Nil);
                      Try
                      StreamDummy.Assign(aAnalyse);
                      OS:=TOleStream.Create(stm);
                      Try
                      OS.WriteComponent(StreamDummy);
                      Finally
                      OS.Free;
                      end;
                      Finally
                      StreamDummy.Free;
                      end;
                      end;</font></pre><br>SaveAnalyseToFile zeigt wie die Sache gespeichert wird. :-) Jens Schuman

                      Comment


                      • #12
                        Hallo Jens, <p> habe bereits das ganze mit ADO (so wie Andreas es vorgeschlagen hat) realisiert. Finde Deinen Beitrag super, denn viele haben noch Probleme mit TCollection und TCollectionItem.<br>Laut meinem Wissen befindet sich noch kein so gutes Beispiel hier im Forum. <br>Hilft bestimmt vielen weiter. Danke dafür

                        Comment


                        • #13
                          Hallo Elmar,<br>vielen Dank. <br>Das interessante an TCollection und TCollectionItem ist, das jedes TCollectionItem wieder eine TCollection TCollectionItem Kombination enthalten kann. Auf diese Weise kann man sich die komplexesten Baumstrukturen ausdenken, die dann einfach komplett gespeichert wird. Ohne das man sich darüber gedanken machen muß. Ich denke, wenn ein Listenelement eine Liste enthalten kann, ist das speichern in eine Datenbank etwas komplizierter<br>:-) Jens Schuman

                          Comment

                          Working...
                          X