AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Projekte UniversalDataUnit
Thema durchsuchen
Ansicht
Themen-Optionen

UniversalDataUnit

Ein Thema von milos · begonnen am 12. Mär 2015 · letzter Beitrag vom 18. Mär 2015
Antwort Antwort
Benutzerbild von milos
milos
Registriert seit: 14. Jul 2008
Hallo DP'ler,

ich habe immer wieder mit den verschiedensten Konfigurationsdaten zu tun und habe mich entschieden eine Universelle Unit dafür zu schreiben und sie zu erweitern wann immer es nötig ist.

Da ich bei meinem neusten Projekt mit INI-Dateien zu tun habe, habe ich nun angefangen die Unit zu programmieren.
Der Code darf für alles verwendet werden, da er eigentlich keinen grossen Wert hat - könnte höchstens ne halbe bis ganze Stunde Programmierarbeit ersparen

Delphi-Quellcode:
// --------------------------------------------------------------------------
// UniversalConfigUnit
// Version 0.1DPP (DelphiPraxis Preview)
// --------------------------------------------------------------------------
// There is no limitation with this code.
// If you want to use, change and/or upload the code, just do it =)
// --------------------------------------------------------------------------
// Features:
// - INI Files
// - Create INI Object
// - Load File to Object
// - Parse String/TStringList to Object
// - Save Object to File
// --------------------------------------------------------------------------
// Authors:
// Stanojevic Milos
// Email: contact@svr2k.de
//
// (add yourself here)
// --------------------------------------------------------------------------

unit UniversalConfigUnit;

interface

uses
  Generics.Collections,
  Classes,
  SysUtils,
  System.RegularExpressions;

type
  { INI CLASSES START }
  TIniSection = class
  private
    FValues : TDictionary<string, string>;
    function FGetValue(AKey: string) : string;
    procedure FSetValue(AKey : string; AProperty : string);
  public
    Title : string;
    constructor Create(ATitle : string);
    function ContainsKey(AKey : string): Boolean;
    property Value[AKey : string] : string read FGetValue write FSetValue; default;
  end;

  TIniObject = class
  private
    FSections : TList<TIniSection>;
    function FGetSection(AIndex : string) : TIniSection;
    function FGetValue(ASection, AKey : string) : string;
    function FGetTitle(AIndex : integer) : string;
  public
    constructor Create();
    procedure Parse(AStringList : TStringList);
    procedure LoadFromFile(AFileName : string);
    procedure SaveToFile(AFileName : string);
    function ContainsSection(ASection : string) : Boolean;
    procedure AddSection(AIniSection : TIniSection);
    procedure AddValue(ASection, AKey, AProperty : string); overload;
    procedure AddValue(ASection, AKey, AProperty : string; AAutoCreate : boolean); overload;
    property Section[Index : string] : TIniSection read FGetSection; default;
    property Value[index, Index2: string] : string read FGetValue;
    property Title[index : integer] : string read FGetTitle;
  end;
  { INI CLASSES END }

implementation

{ TIniConfig }

procedure TIniObject.AddSection(AIniSection: TIniSection);
var
  LIndex : integer;
begin
  LIndex := FSections.Add(AIniSection);
end;

procedure TIniObject.AddValue(ASection, AKey, AProperty: string);
begin
  Section[ASection].FValues.Add(AKey, AProperty);
end;

procedure TIniObject.AddValue(ASection, AKey, AProperty: string; AAutoCreate : boolean);
begin
  if AAutoCreate then
  begin
    if ContainsSection(ASection) then
      AddValue(ASection, AKey, AProperty);
  end
  else
  begin
    AddSection(TIniSection.Create(ASection));
    AddValue(ASection, AKey, AProperty);
  end;
end;

function TIniObject.ContainsSection(ASection: string): Boolean;
var
  c: Integer;
begin
  for c := 0 to FSections.Count-1 do
    if FSections[c].Title = ASection then
      Result := true;
end;

constructor TIniObject.Create;
begin
  FSections := TList<TIniSection>.Create();
end;

function TIniObject.FGetSection(AIndex : string): TIniSection;
var
  c: Integer;
begin
  for c := 0 to FSections.Count-1 do
    if FSections[c].Title = AIndex then
      Result := FSections[c];


  if Result = nil then
    raise Exception.Create(AIndex + ' konnte nicht gefunden werden');
end;

function TIniObject.FGetTitle(AIndex: integer): string;
begin
  Result := FSections[AIndex].Title;
end;

function TIniObject.FGetValue(ASection, AKey: string): string;
begin
  Result := Section[ASection][AKey];
end;

procedure TIniObject.LoadFromFile(AFileName: string);
var
  LStringList : TStringList;
begin
  if FileExists(AFileName) then
  begin
    LStringList := TStringList.Create;
    LStringList.LoadFromFile(AFileName);
    Parse(LStringList);
  end
  else
    raise Exception.Create(AFileName + ' wurde nicht gefunden');

  LStringList.Free;
end;

procedure TIniObject.Parse(AStringList: TStringList);
var
  LLine : string;
  LParts : TStringList;
  c: Integer;
begin
  LParts := TStringList.Create;
  for c := 0 to AStringList.Count - 1 do
  begin
    LLine := AStringList[c];

    if Length(LLine) < 1 then
    Continue;

    if LLine[1] = ';then
      Continue
    else if (LLine[1] = '[') and (LLine[Length(LLine)] = ']') then
    begin
      AddSection(TIniSection.Create(Copy(LLine,2,Length(LLine)-2)));
      Continue;
    end
    else if (TRegEx.IsMatch(LLine, '^[a-zA-Z0-9]*=[a-zA-Z0-9(./)]*$')) then
    begin
      LParts.Clear;
      LParts.StrictDelimiter := true;
      LParts.Delimiter := '=';
      LParts.DelimitedText := LLine;

      FSections[FSections.Count-1].FValues.Add(LParts[0],LParts[1]);
    end
    else
      raise Exception.Create('Fehler beim laden der INI-Datei.' + #10 +
                             'Zeile: ' + IntToStr(c + 1) + #10 +
                             '"' + LLine + '" ist keine gültige INI Anweisung.');


  end;

  LParts.Free;
end;

procedure TIniObject.SaveToFile(AFileName: string);
var
  LStringList : TStringList;
  LPair : TPair<string, string>;
  c: Integer;
begin
  LStringList := TStringList.Create;

  for c := 0 to FSections.Count-1 do
  begin
    LStringList.Add('[' + FSections[c].Title + ']');
    for LPair in FSections[c].FValues do
      LStringList.Add(LPair.Key + '=' + LPair.Value);


    LStringList.Add('');
  end;

  LStringList.SaveToFile(AFileName);
  LStringList.Free;
end;

{ TIniSection }

function TIniSection.ContainsKey(AKey : string): Boolean;
var
  c: Integer;
begin
  Result := FValues.ContainsKey(AKey);
end;

constructor TIniSection.Create(ATitle : string);
begin
  Title := ATitle;
  FValues := TDictionary<string, string>.Create;
end;


function TIniSection.FGetValue(AKey: string): string;
begin
  Result := FValues[AKey];
end;

procedure TIniSection.FSetValue(AKey, AProperty: string);
begin
  if ContainsKey(AKey) then
    FValues[AKey] := AProperty;
end;

end.
Ich habe versucht den Code so kurz wie möglich zu halten. Es gibt auch viel Optimierungspotenzial aber da ich nun Feierabend(mittag ) habe, werde ich erst morgen weiter machen können.

Ich freue mich über jede Verbesserungsvorschläge oder sonstigen Anmerkungen.

Freundliche Grüsse

Geändert von milos ( 4. Apr 2015 um 10:03 Uhr)
 
Benutzerbild von Sir Rufo
Sir Rufo

 
Delphi 10 Seattle Enterprise
 
#2
  Alt 12. Mär 2015, 13:49
Willst du die ganzen MemLeaks noch beseitigen?
  Mit Zitat antworten Zitat
Benutzerbild von milos
milos

 
Delphi 11 Alexandria
 
#3
  Alt 12. Mär 2015, 18:45
Hi

Ja die paar MemoryLeaks werden noch gefixt Habe mir auch noch ein paa Sachen überlegt die man besser machen könnte. Hat sonst noch jemand Vorschläge oder Kritikpunkte? Bin für alles offen
Milos
  Mit Zitat antworten Zitat
Der schöne Günther

 
Delphi 10 Seattle Enterprise
 
#4
  Alt 12. Mär 2015, 18:57
Ich habe die Implementation jetzt noch nicht gelesen, aber ich verstehe nicht, was denn nun eine TIniSection und was ein TIniObject ist. Eine Sektion scheint einen änderbaren Titel und String-String-Paare zu besitzen. Ein TIniObject scheint nun beliebig viele dieser Sektionen sowie beliebig viele Titel (?) zu beinhalten. Wenn ich es richtig verstehe wäre doch "TIniFile" ein passenderer Name, oder?

Vorausgesetzt ich liege nicht meilenweit daneben, verstehe ich eins noch nicht: Wo ist nun der Unterschied von TIniObject zum in Delphi bereits vorhandenen TIniFile?
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

 
Delphi 10.1 Berlin Enterprise
 
#5
  Alt 12. Mär 2015, 18:58
Kannst du mal den Vorteil gegenüber einer TMemIniFile erörtern? Denn für mich sieht das nach einem flüchtigen Blick genau danach aus.
Stefan
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

 
Delphi 2007 Professional
 
#6
  Alt 15. Mär 2015, 20:07
Füg' doch noch die Methode LoadFromStream() und SaveToStream() hinzu.
Denn ein File ist ja nur die Spezialisierung eines Streams; und wer weiß vielleicht willst du die Konfiguration ja mal von einem Webserver auslesen.
Natürlich rufen die Methoden LoadFromFile() und SaveToFile() ihre allgemeineren Streamvarianten auf und übergeben ein FileStream-Objekt.


Delphi-Quellcode:
procedure TIniObject.Parse(AStringList: TStringList); // falsch: TStringList ist zu spezifisch
procedure TIniObject.Parse(AStringList: TStrings); // richtig: TStrings bietet dem Aufrufer alle Möglichkeiten
  Mit Zitat antworten Zitat
Benutzerbild von milos
milos

 
Delphi 11 Alexandria
 
#7
  Alt 18. Mär 2015, 16:23
Ich habe die Implementation jetzt noch nicht gelesen, aber ich verstehe nicht, was denn nun eine TIniSection und was ein TIniObject ist. Eine Sektion scheint einen änderbaren Titel und String-String-Paare zu besitzen. Ein TIniObject scheint nun beliebig viele dieser Sektionen sowie beliebig viele Titel (?) zu beinhalten. Wenn ich es richtig verstehe wäre doch "TIniFile" ein passenderer Name, oder?

Vorausgesetzt ich liege nicht meilenweit daneben, verstehe ich eins noch nicht: Wo ist nun der Unterschied von TIniObject zum in Delphi bereits vorhandenen TIniFile?
Kannst du mal den Vorteil gegenüber einer TMemIniFile erörtern? Denn für mich sieht das nach einem flüchtigen Blick genau danach aus.
Ihr habt natürlich recht, dass es schon dutzende Varianten gibt eine INI-Datei zu laden und zu speichern, jedoch ist mein Ziel eine schlanke Unit für verschiedene Konfigurationsdaten (XML, JSON usw [auch für Custom-Configs]) zu erstellen. Ich denke mal Vorteile wird meine Unit bisher noch keine haben (ausser dass sie schlank ist? ) aber wie gesagt, vielleicht mags der eine oder andere brauchen und wenn nicht, dann halt nicht Ausserdem ist es doch meistens ziemlich entspannend nach mehreren Stunden krampfen in einem etwas grösserem Projekt eine kleine, simple aber funktionstüchtige Unit zu schreiben.


Füg' doch noch die Methode LoadFromStream() und SaveToStream() hinzu.
Denn ein File ist ja nur die Spezialisierung eines Streams; und wer weiß vielleicht willst du die Konfiguration ja mal von einem Webserver auslesen.
Natürlich rufen die Methoden LoadFromFile() und SaveToFile() ihre allgemeineren Streamvarianten auf und übergeben ein FileStream-Objekt.


Delphi-Quellcode:
procedure TIniObject.Parse(AStringList: TStringList); // falsch: TStringList ist zu spezifisch
procedure TIniObject.Parse(AStringList: TStrings); // richtig: TStrings bietet dem Aufrufer alle Möglichkeiten
Danke für die Tipps, werde ich beachten und einbauen wenn es die Zeit erlaubt
Zufälligerweise hatte ich schon ein Problem mit der StringList

Habe auch noch etwas kleines aber doofes bemerkt:
Erstmal ein schneller Crashkurs:
TIniObject beinhaltet die ganze IniDatei mit allen Sektionen.
TIniSection ist eine Sektion bei der, der Titel immer mit eckigen Klammern umgeben sind und dessen Eigenschaften unten aufgeführt sind.

Es gibt folgende Wege eine Eigenschaft auszulesen:
Delphi-Quellcode:
  IniObject[Sektion][Eigenschaftsvariable] : Eigenschaftswert als String

  IniObject.Value[Sektion, Eigenschaftsvariable] : Eigenschaftswert als String
Ich denke mal die Value variante wird raus genommen, da sie eigentlich unnötig ist. ^^

Freundliche Grüsse
Milos
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:54 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz