2009-08-15 2 views
2

명시 적으로 "Record.somefield"를 수행하지 않고 레코드를 가져 와서 모든 필드를 프로그램 적으로 처리하는 방법이 있습니까?델파이 : 알 수없는 구조의 레코드 구문 분석

내가하고 싶은 것은 INI 파일로 레코드를 저장 /로드하는 일반화 된 기능으로, 제거하거나 추가 할 때마다 저장 /로드 기능을 다시 작성할 필요없이 레코드 구조에서 필드를 추가하거나 제거 할 수 있습니다 필드를 레코드에 추가하십시오. 즉

나는이 (의사 코드)과 같은 몇 가지 방법으로 반복 할 : 내가 제기 형식화를 저장할 수 없기 때문에

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, 내가이 일을하려는 이유는 비 한정 문자열을 사용합니다. 또한 파일이 사람이 읽을 수 있다는 것이 나에게 유익합니다. 그래서 나는 그들과 INI 파일을 저장하고있다.

+0

는 DFM 파일을 가져 오는 예를 게시; 곧 나의 blog에 그것을 게시 할 것이다. –

+0

이 질문에 대한 받아 들인 코멘트를보고 싶을 수도 있습니다. http://stackoverflow.com/questions/1293504/what-is-the-best-way-to-serialize-delphi-application-configuration RTTI에 직접 참여하십시오. –

답변

0

나는 이런 종류의 레코드를 클래스로 변환 할 것을 제안합니다.

5

그럴 가능성은 없습니다. 레코드에 런타임 유형 정보가 연결되어 있지 않습니다. 대신 게시 된 속성이있는 클래스를 사용했다면 jvcl의 TApplicationStorage/TFormStorage 구성 요소, Delphi DFM 스트리밍 시스템 및 다양한 인터넷 사이트를 참조하십시오.

+0

그 레코드가 속성을 가질 수있는 델파이의 고급 레코드 구조로 작동합니까? –

+0

레코드가 속성을 게시 할 수 있다고 생각하지 않습니다. 개인 및 공개 만 가능합니다 (상속을 지원하지 않기 때문에 보호되지 않습니다). – dummzeuch

2

이름/값 쌍으로 채워진 TStringList를 사용할 수 있습니다.

미리 구조를 알 필요는 없으며, 요소는 한 번에 하나씩 색인을 통해 쉽게 접근 할 수 있으며 ini 파일에 저장하기에 완벽하게 적합합니다.

TIniFile.ReadSectionValues를 사용하여 ini 파일의 섹션에서 TStringList를 채 웁니다.

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

그 전에 (실제로는 CSV로 서식을 지정했지만) 문자열에 대한 모든 데이터 변환으로 인해 이점이 줄어 들었습니다.이 경우에는이 개념에 맞는 서식을 추가해야하는 여러 줄 문자열이 있습니다. –

+0

TStringList 대신 TMemIniFile을 사용하여 일부 변환을 피할 수 있습니다. 멀티 라인 문자열을위한 해결책은 로딩 후에 중성 문자 (# 7)로 CRLF를 저장하고 바꿔 넣는 것입니다. –

0

예.하지만 레코드를 개체로 랩핑하고 필드를 속성으로 게시해야합니다. 그런 다음 속성을 반복하여 INI 파일을 읽고 쓸 수 있습니다. 나는 이것을 많이했는데, RTTI는 매우 유용합니다. Bri

1

레코드는 RTTI를 가질 수 있지만 참조 카운트 필드 (문자열 또는 인터페이스 등)가 하나 이상인 경우에만 가능합니다. 그리고 그 기록 RTTI는 매우 제한적이므로 어떤 도움도 안됩니다.

Delphi Live에 Delphi 2010에서 RTTI가 많이 확장되어 레코드 RTTI가 확장되었을 수도 있습니다.

TComponent에서 클래스를 파생시키고 내장 된 스트리밍 메커니즘을 사용하거나 RTTI를 사용하여 INI 파일에서 가져 오는 것이 좋습니다. 훨씬 쉬워졌습니다!

--jeroen

편집 : 예

첫 번째 구성 요소 : 다음

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. 

결과 DFM 파일 : DFM 파일을 편집 할 합리적으로 쉽다는 것을

object TIntegerValueComponent 
    IntegerValue = 33 
end 

, ini 파일과 마찬가지로 오류가 발생할 수 있으므로 임시 사용자가 편집하지 않는 것이 좋습니다.

스트림으로/스트리밍으로 기본 스트리밍을 수행하고 이진 형식을 텍스트 형식으로 변환합니다 (메모리가 올바르게 작동하는 경우 텍스트 형식은 Delphi 5 이후 기본 설정 임). 읽는 것이 훨씬 쉬워 짐).

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. 

그리고,이 예 (제 1 형태의 코드, 그 형태 DFM)을 작동해야 가장 중요한 라인 있는 registerClass (TIntegerValueComponent)이고;, 잊기 쉽습니다. 나머지 코드는 꽤 간단합니다.

또한 보너스로 구성 요소를 클립 보드에 복사하여 붙여 넣을 수있는 방법을 알 수 있습니다. 바이너리 형식을 사용하여 클립 보드에서 /로 스트리밍됩니다.

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; 

마지막으로 양식 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

Tcomponent 스트리밍 메커니즘 또는 RTTI를 사용하여 INI 파일로 가져 오는 것이 무슨 뜻인지 예를 들려 줄 수 있습니까? 웹 참조 링크가 좋습니다. –

관련 문제