2009-08-15 14 views
2

Gibt es eine Möglichkeit, einen Datensatz aufzunehmen und alle Felder programmatisch zu durchsuchen, ohne "Record.somefield" explizit auszuführen?Delphi: Analysieren eines Datensatzes mit unbekannter Struktur

Was ich tun möchte, ist eine verallgemeinerte Funktion zum Speichern/Laden eines Datensatzes als INI-Datei, so dass ich Felder aus meiner Datensatzstruktur hinzufügen oder entfernen kann, ohne jedes Mal die Speicher-/Ladefunktionen neu zu schreiben Felder zum Datensatz.

Mit anderen Worten möchte ich in irgendeiner Art und Weise wie diese (Pseudocode) iterieren:

THuman = record 
    age: integer; 
    country: string ; 
end; 
... 
myPerson: THuman; 
... 
    foreach Field in myPerson do 
    begin 
     if Field.value is String then ini.WriteString('Group', Field.Name, Field.value); 
     if Field.value is integer then ini.WriteInteger('Group', Field.Name, Field.value); 
    end; 

BTW, der Grund, warum ich dies tun möchte, ist, weil ich nicht eine typisierte eingereicht speichern kann, wenn Verwenden von nicht begrenzten Strings. Auch ist es vorteilhaft für mich, dass die Dateien für Menschen lesbar sind. Also speichere ich sie und INI-Dateien.

+0

posted ein Beispiel für das Erhalten der DFM-Dateien aus; werde das in Kürze auf meinem Blog posten. –

+0

Sie können auch den akzeptierten Kommentar für diese Frage ansehen: http://stackoverflow.com/questions/1293504/what-is-the-best-way-to-serialize-delphi-application-configuration zeigt es Ihnen wie um das RTTI-Zeug selbst zu machen. –

Antwort

0

würde ich eher empfehlen Ihnen, diese Art von Datensätzen Klassen

5

Keine Chance, dass zu konvertieren. Ein Datensatz enthält keine Informationen zum Laufzeittyp. Wenn Sie stattdessen eine Klasse mit veröffentlichten Eigenschaften verwenden, können Sie die Beispiele in den Komponenten tapplicationStorage/TFormStorage im jvcl, im Delphi DFM-Streaming-System und auf verschiedenen Websites nachschlagen.

+0

Würde das mit der erweiterten Datensatzstruktur in Delphi funktionieren, die es Records erlaubt, Eigenschaften zu haben? –

+0

Ich glaube nicht, dass ein Datensatz Eigenschaften veröffentlicht haben kann, sie können nur privat und öffentlich sein (geschützt macht keinen Sinn, weil sie Vererbung nicht unterstützen). – dummzeuch

2

Sie könnten einfach eine TStringList verwenden, die mit Name/Wert-Paaren gefüllt ist.

Sie müssen die Struktur nicht im Voraus kennen, die Elemente können einfach einzeln durchlaufen werden oder nach Index sortiert werden, und sie ist perfekt zum Speichern in INI-Dateien geeignet.

Verwenden Sie TIniFile.ReadSectionValues, um Ihre TStringList aus einem Abschnitt in Ihrer Ini-Datei zu füllen.

var 
    i: Integer; 
begin 
    FList := TStringList.Create; 
    FList.Add('a=one'); 
    FList.Add('b=two'); 
    FList.Add('c=three'); 

    Label1.Caption := FList.Values['b']; 

    for i := 0 to FList.Count - 1 do 
    Memo1.Lines.Add(FList.Names[i] + ' - ' + FList.Values[FList.Names[i]]); 
+1

Ich habe das schon mal ausprobiert (eigentlich als CSV formatiert), aber die Vorteile sind aufgrund der Datenkonvertierung auf Strings verringert, und in diesem Fall habe ich mehrzeilige Strings, die weitere Formatierungen benötigen, um zu diesem Konzept zu passen. –

+0

Sie können einige der Konvertierungen vermeiden, indem Sie eine TMemIniFile anstelle einer TStringList verwenden. Die Lösung für mehrzeilige Strings könnte darin bestehen, CRLF vor dem Speichern mit einem neutralen Zeichen (# 7) zu beeinflussen und nach dem Laden wieder auszutauschen. –

0

Ja, aber Sie müssen den Datensatz in ein Objekt umbrechen und die Felder als Eigenschaften veröffentlichen. Sie können dann die Eigenschaften durchlaufen, um eine INI-Datei zu lesen und zu schreiben. Ich habe das schon oft gemacht, RTTI ist sehr nützlich. Bri

1

Datensätze können RTTI haben, aber nur wenn mindestens ein Referenzfeld (wie String oder Interface) vorhanden ist. Und diese Rekord RTTI ist sehr begrenzt, also wird es dir nicht weiter helfen.

Unter Delphi Live wurde gezeigt, dass RTTI in Delphi 2010 stark erweitert wird, so dass es möglicherweise auch RTTI-Datensätze erweitert hat.

Ich schlage vor, dass Sie eine Klasse von TComponent ableiten und verwenden Sie den integrierten Streaming-Mechanismus, oder verwenden RTTI, um es zu/von einer INI-Datei zu erhalten. Viel einfacher!

--jeroen

Edit: Beispiel

Zuerst wird die Komponente:

unit IntegerValueComponentUnit; 

interface 

uses 
    Classes; 

type 
    TCustomIntegerValueComponent = class(TComponent) 
    strict private 
    FIntegerValue: Integer; 
    strict protected 
    function GetIntegerValue: Integer; virtual; 
    procedure SetIntegerValue(const Value: Integer); virtual; 
    public 
    property IntegerValue: Integer read GetIntegerValue write SetIntegerValue; 
    end; 

    TIntegerValueComponent = class(TCustomIntegerValueComponent) 
    published 
    property IntegerValue; 
    end; 

implementation 

function TCustomIntegerValueComponent.GetIntegerValue: Integer; 
begin 
    Result := FIntegerValue; 
end; 

procedure TCustomIntegerValueComponent.SetIntegerValue(const Value: Integer); 
begin 
    FIntegerValue := Value; 
end; 

end. 

Dann wird die resultierende DFM-Datei:

object TIntegerValueComponent 
    IntegerValue = 33 
end 

Das DFM-Datei ziemlich einfach zu bearbeiten Wie bei INI-Dateien können Sie Fehler machen, daher ist es nicht empfehlenswert, sie von gelegentlichen Benutzern zu bearbeiten.

Sie benötigen diese Einheit, um loszulegen, das grundlegende Streaming zu/von Streams und die Umwandlung des Binärformats in das Textformat (wenn der Speicher mir recht tut, ist das Textformat seit Delphi 5 der Standard) wie es viel einfacher zu lesen ist).

unit ComponentDfmUnit; 

interface 

uses 
    Classes, IntegerValueComponentUnit; 

type 
    TComponentDfm = class 
    public 
    class function FromDfm(const Dfm: string): TComponent; static; 
    class function GetDfm(const Component: TComponent): string; static; 
    class function LoadFromDfm(const FileName: string): TComponent; static; 
    class procedure SaveToDfm(const Component: TComponent; const FileName: string); static; 
    end; 

implementation 

class function TComponentDfm.FromDfm(const Dfm: string): TComponent; 
var 
    MemoryStream: TMemoryStream; 
    StringStream: TStringStream; 
begin 
    MemoryStream := TMemoryStream.Create; 
    try 
    StringStream := TStringStream.Create(Dfm); 
    try 
     ObjectTextToBinary(StringStream, MemoryStream); 
     MemoryStream.Seek(0, soFromBeginning); 
     Result := MemoryStream.ReadComponent(nil); 
    finally 
     StringStream.Free; 
    end; 
    finally 
    MemoryStream.Free; 
    end; 
end; 

class function TComponentDfm.GetDfm(const Component: TComponent): string; 
var 
    MemoryStream: TMemoryStream; 
    StringStream: TStringStream; 
begin 
    MemoryStream := TMemoryStream.Create; 
    try 
    MemoryStream.WriteComponent(Component); 
    StringStream := TStringStream.Create(''); 
    try 
     MemoryStream.Seek(0, soFromBeginning); 
     ObjectBinaryToText(MemoryStream, StringStream); 
     Result := StringStream.DataString; 
    finally 
     StringStream.Free; 
    end; 
    finally 
    MemoryStream.Free; 
    end; 
end; 

class function TComponentDfm.LoadFromDfm(const FileName: string): TComponent; 
var 
    DfmStrings: TStrings; 
begin 
    DfmStrings := TStringList.Create; 
    try 
    DfmStrings.LoadFromFile(FileName); 
    Result := TComponentDfm.FromDfm(DfmStrings.Text); 
    finally 
    DfmStrings.Free; 
    end; 
end; 

class procedure TComponentDfm.SaveToDfm(const Component: TComponent; const FileName: string); 
var 
    DfmStrings: TStrings; 
begin 
    DfmStrings := TStringList.Create; 
    try 
    DfmStrings.Text := TComponentDfm.GetDfm(Component); 
    DfmStrings.SaveToFile(FileName); 
    finally 
    DfmStrings.Free; 
    end; 
end; 

end. 

Und dann sollte dieses Beispiel arbeiten (zunächst den Formularcode, dann ist die Form DFM): Die wichtigste Zeile ist Register (TIntegerValueComponent);, wie es leicht zu vergessen ist. Der Rest des Codes ist ziemlich geradlinig.

Als Bonus sehen Sie auch, wie Sie eine Komponente in die Zwischenablage kopieren und wieder einfügen können. Es streamt zu/aus der Zwischenablage im Binärformat.

const 
    FileName = 'IntegerValue.dfm'; 

procedure TStreamingDemoForm.ButtonEnabledTimerTimer(Sender: TObject); 
begin 
    SaveButton.Enabled := SaveStyleRadioGroup.ItemIndex <> -1; 
    LoadButton.Enabled := SaveButton.Enabled; 
    if SaveStyleRadioGroup.ItemIndex = 0 then 
    LoadButton.Enabled := FileExists(FileName); 
    if SaveStyleRadioGroup.ItemIndex = 1 then 
    LoadButton.Enabled := Clipbrd.Clipboard.HasFormat(CF_COMPONENT); 
end; 

procedure TStreamingDemoForm.LoadButtonClick(Sender: TObject); 
var 
    IntegerValueComponent: TIntegerValueComponent; 
begin 
    IntegerValueComponent := nil; 
    if SaveStyleRadioGroup.ItemIndex = 0 then 
    IntegerValueComponent := LoadUsingFileStream() 
    else 
    IntegerValueComponent := LoadUsingClipboard(); 
    try 
    if Assigned(IntegerValueComponent) then 
     Log('Loaded: %d', [IntegerValueComponent.IntegerValue]) 
    else 
     Log('nil during Load'); 
    finally 
    IntegerValueComponent.Free; 
    end; 
end; 

function TStreamingDemoForm.LoadUsingClipboard: TIntegerValueComponent; 
var 
    Component: TComponent; 
begin 
    Result := nil; 
    RegisterClass(TIntegerValueComponent); 
    Component := Clipboard.GetComponent(nil, nil); 
    if Assigned(Component) then 
    if Component is TIntegerValueComponent then 
     Result := TIntegerValueComponent(Component); 
end; 

function TStreamingDemoForm.LoadUsingFileStream: TIntegerValueComponent; 
var 
    Component: TComponent; 
begin 
    Result := nil; 
    RegisterClass(TIntegerValueComponent); 
    Component := TComponentDfm.LoadFromDfm(FileName); 
    if Assigned(Component) then 
    if Component is TIntegerValueComponent then 
     Result := TIntegerValueComponent(Component); 
end; 

procedure TStreamingDemoForm.Log(const Line: string); 
begin 
    LogMemo.Lines.Add(Line); 
end; 

function TStreamingDemoForm.Log(const Mask: string; const Args: array of const): string; 
begin 
    Log(Format(Mask, Args)); 
end; 

procedure TStreamingDemoForm.SaveButtonClick(Sender: TObject); 
var 
    IntegerValueComponent: TIntegerValueComponent; 
begin 
    IntegerValueComponent := TIntegerValueComponent.Create(nil); 
    try 
    IntegerValueComponent.IntegerValue := ValueToSaveSpinEdit.Value; 
    if SaveStyleRadioGroup.ItemIndex = 0 then 
     SaveUsingFileStream(IntegerValueComponent) 
    else 
     SaveUsingClipboard(IntegerValueComponent); 
    Log('Saved: %d', [IntegerValueComponent.IntegerValue]) 
    finally 
    IntegerValueComponent.Free; 
    end; 
end; 

procedure TStreamingDemoForm.SaveUsingClipboard(IntegerValueComponent: TIntegerValueComponent); 
begin 
    Clipboard.SetComponent(IntegerValueComponent); 
end; 

procedure TStreamingDemoForm.SaveUsingFileStream(IntegerValueComponent: TIntegerValueComponent); 
begin 
    TComponentDfm.SaveToDfm(IntegerValueComponent, Filename); 
end; 

Schließlich wird die Form DFM:

object StreamingDemoForm: TStreamingDemoForm 
    Left = 0 
    Top = 0 
    Caption = 'StreamingDemoForm' 
    ClientHeight = 348 
    ClientWidth = 643 
    Color = clBtnFace 
    Font.Charset = DEFAULT_CHARSET 
    Font.Color = clWindowText 
    Font.Height = -11 
    Font.Name = 'Tahoma' 
    Font.Style = [] 
    OldCreateOrder = False 
    DesignSize = (
    643 
    348) 
    PixelsPerInch = 96 
    TextHeight = 13 
    object ValueToSaveLabel: TLabel 
    Left = 14 
    Top = 8 
    Width = 65 
    Height = 13 
    Caption = '&Value to save' 
    FocusControl = ValueToSaveSpinEdit 
    end 
    object ValueToSaveSpinEdit: TSpinEdit 
    Left = 95 
    Top = 5 
    Width = 121 
    Height = 22 
    MaxValue = 0 
    MinValue = 0 
    TabOrder = 0 
    Value = 0 
    end 
    object SaveButton: TButton 
    Left = 14 
    Top = 33 
    Width = 75 
    Height = 25 
    Caption = '&Save' 
    TabOrder = 1 
    OnClick = SaveButtonClick 
    end 
    object LoadButton: TButton 
    Left = 14 
    Top = 64 
    Width = 75 
    Height = 25 
    Caption = '&Load' 
    TabOrder = 2 
    OnClick = LoadButtonClick 
    end 
    object LogMemo: TMemo 
    Left = 14 
    Top = 95 
    Width = 621 
    Height = 245 
    Anchors = [akLeft, akTop, akRight, akBottom] 
    Lines.Strings = (
     'LogMemo') 
    TabOrder = 3 
    end 
    object SaveStyleRadioGroup: TRadioGroup 
    Left = 95 
    Top = 30 
    Width = 121 
    Height = 59 
    Caption = 'Save st&yle' 
    Items.Strings = (
     'Value.dfm' 
     'Clipboard') 
    TabOrder = 4 
    end 
    object ButtonEnabledTimer: TTimer 
    Interval = 100 
    OnTimer = ButtonEnabledTimerTimer 
    Left = 270 
    Top = 6 
    end 
end 
+0

Können Sie mir ein Beispiel dafür geben, was Sie mit dem Tcomponent-Streaming-Mechanismus oder der RTTI meinen, um es in INI-Dateien zu bekommen? Ein Web-Verweis-Link wäre in Ordnung. –

Verwandte Themen