Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Objektorientiertes TIniFile-Replacement mit Unicode-Fähigkeit (https://www.delphipraxis.net/171091-objektorientiertes-tinifile-replacement-mit-unicode-faehigkeit.html)

Codehunter 19. Okt 2012 15:13

Objektorientiertes TIniFile-Replacement mit Unicode-Fähigkeit
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallöche...

Ich verwende als Konfiguration in meinen Projekten am liebsten Ini-Dateien statt der Registry. Dabei hat mich eigentlich immer gestört, dass TIniFile
  1. keinen direkten Zuriff auf Einträge in der Ini bietet sondern mehr oder weniger nur eine in eine Klasse verpackte prozedurale Programmierung hat
  2. Nicht wirklich eine Streaming-Fähigkeit hat, man kann nicht direkt aus Streams lesen oder in Streams speichern.
  3. (zumindest in D7) nicht Unicode-fähig ist
  4. Keine umkonfigurierbaren Kommentar-Einleit-Zeichen hat
  5. Keine Zeilenumbrüche innerhalb von Variablenwerten unterstützt
  6. Keine Funktion zum regelmäßigen automatischen Speichern hat
Darum hab ich mal ein bissi was gebastelt. Herausgekommen ist eine Klasse, die einen Großteil der Properties und Methoden von TIniFile bietet (also zu 99% kompatibel sein sollte), per Compilerschalter zwischen einer Unicode- und einer ANSI-Version umschaltbar ist und auf die einzelnen Sections und Idents direkt zugreifen kann. Ein Beispiel:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  Ini: TUniObjectIni;
begin
  Ini:= TUniObjectIni.Create('C:\Test.txt');
  try
    Ini['Section1']['Ident1'].Str:= 'Test 1';
    Ini['Section2']['Ident1'].Str:= 'Zeile 1' + #13#10 + 'Zeile 2';
    Ini['Section2']['Number'].Int:= 1234;
    Ini['Section2']['Float'].Float:= 12.34;
    Ini['Section2']['Timestamp'].DateTime:= Now;
  finally
    Ini.UpdateFile;
    Ini.Free;
  end;
end;
Am Rande bemerkt verwendet TUniObjectIni nicht das Windows-API für die Dateigeschichten (Stichwort WritePrivateProfileString) sondern arbeitet komplett mit einer StringList. Dadurch sollte der Code auch relativ leicht zu Lazarus portierbar sein. Es wird aber nur beim Laden und Speichern von Dateien auf der StringList rumfuhrwerkt, alles andere passiert komplett im Speicher.

Als Unicode-Backend verwende ich in dem Fall SynUnicode (siehe SynEdit-Komponenten bei Sourceforge), man könnte aber genausogut jede andere Implementierung einer Unicode-fähigen StringList verbauen. Die jetzige ist jedenfalls schlau genug, um sowohl ANSI- als auch Unicode-codierte Dateien zu laden. Einmal als ANSI geladene Dateien bleiben auch beim Speichern ANSI, es sei denn man fügt Unicode ein.

Schauts euch mal an und gebt mal ein bisschen Feedback dazu.

Greets
Cody

himitsu 19. Okt 2012 15:28

AW: Objektorientiertes TIniFile-Replacement mit Unicode-Fähigkeit
 
Dein Ini.UpdateFile gehört aber eigentlich noch vor das Finally :zwinker:


Zitat:

Dadurch sollte der Code auch relativ leicht zu Lazarus portierbar sein.
Lazazus sollte eigentlich auch in der Lage sein, auf die WinAPI zuzugreifen. :stupid:


Ich würde dir empfehlen keine lokalen Sprachinformationen zu nutzen, also kein DateToStr oder FloatToStr usw.
Ich weiß grde nicht, ob man im D7 bei diesen Funktionen ein FormtSettings angeben kann, aber wenn, dann solltest du dieses verwenden und dann für Fließkommazahlen und Zeit-/Datumswerte ein einheitliches Format verwenden.
Sonst gibt es das bekannte Problem, daß man die INI eventuell nicht wieder auslesen kann, wenn jemand die Sprache umstellt oder man sie auf einem anderem Rechner auslesen will.

z.B. Dezimaltrenner als "Punkt" und für's Datum macht sich "yyyy-mm-dd", bzw. "yyyy/mm/dd" und "hh:mm:ss" recht gut



Nein, es müssen nicht immer #13#10 als Zeilenumbruch vorkommen.
Was mag wohl passieren, wenn z.b. nur eine #10 vorkommt?

Nja, im Grunde müßtest du eigentlich nochwas abfangen, denn was passiert wohl, wenn ein \n als Text in AValue drinsteht?
wie z.B. ein "ja\nein"

So, der nachfolgende Code normalisiert alle "bekannten" Zeilenumbrüche Windows/Linux/Mac.
Oder du behandelst alle Steuerzeichen, wie #10, #13, #0 einzeln.
#0 = \0
\ = \\
#9 = \t
#10 = \n
#13 = \r
...
Delphi-Quellcode:
procedure TUniObjectIniValue.SetString(AValue: TUniObjectIniString);
begin
  SL:= TUniObjectIniStringList.Create;
  try
    SL.Text:= AValue;
    {$IFDEF UNICODE}
      Value:= UnicodeStringReplace(SL.Text, sLineBreak, '\n', [rfReplaceAll]);
    {$ELSE}
      Value:= StringReplace(SL.Text, sLineBreak, '\n', [rfReplaceAll]);
    {$ENDIF}
  finally
    SL.Free;
  end;
end;

function TUniObjectIniValue.GetString: TUniObjectIniString;
begin
  {$IFDEF UNICODE}
  result:= UnicodeStringReplace(FValue, '\n', sLineBreak, [rfReplaceAll]);
  {$ELSE}
  result:= StringReplace(FValue, '\n', sLineBreak, [rfReplaceAll]);
  {$ENDIF}
end;

Popov 19. Okt 2012 16:27

AW: Objektorientiertes TIniFile-Replacement mit Unicode-Fähigkeit
 
Zitat:

Zitat von Codehunter (Beitrag 1187627)
Hallöche...

Ich verwende als Konfiguration in meinen Projekten am liebsten Ini-Dateien statt der Registry. Dabei hat mich eigentlich immer gestört, dass TIniFile
  1. keinen direkten Zuriff auf Einträge in der Ini bietet sondern mehr oder weniger nur eine in eine Klasse verpackte prozedurale Programmierung hat
    ...
...

Was dein erstes Problem angeht, so läßt es sich insoweit umgehen, als dass du dir eine eigene Unit mit vereinfachtem INI Zugang schreibst. Das habe ich auch mal vor Jahren gemacht und eine Unit geschrieben die mir einen sehr simplen Zugriff ermöglicht. Was nicht bedeuten soll, dass TIniFile kompliziert ist, aber ich hab mich des Drumherum entledigt. Wenn es dich interessiert, such mal im Internet nach EasyIniAppExe.pas. Ist die Unit eingebunden, liefert sie ein Dutzend Funktionen mit denen ich meine Daten direkt speichern kann. Der besondere Vorteil ist eine zweite Unit mit gleichen Funktionen. Tausche ich die, speichert das Programm die Daten nun in der Registry. Ein einfacher Unit-Tausch reicht.

Worauf ich hinaus will, wenn du ein permanentes Problem hast, dann löse es. So unscheinbar meine Unit auch ist, fast jedes meiner Programme enthält sie um meine Einstellungen zu speichern. Das entspannt.

himitsu 19. Okt 2012 16:35

AW: Objektorientiertes TIniFile-Replacement mit Unicode-Fähigkeit
 
Ich würde aber nicht die Unit direkt austauschen.

Es gibt eine Unit/Klasse mit den Basisfunktionen.
Davon wird dann z.B. jeweils Einie für die INI und eine für die Registry abgeleitet.

Alles was nun auf diese Funktionen zugreifen will, bekommt ein Property vom Baisityp.

Nun wird man beim Erstellen/Laden des Programms die Instanz (INI oder Regisrty) erstellt und allen anderen Teilen zugewiesen, welche sie benötigen.
So kann man auch noch beim Laden entscheiden und z.B. so sein Programm entweder für eine portable oder eine lokale Speicherung einstellen.

Im Notfall gibt es eben irgendwo ein "globales" Singleton mit den Basisfunktionen, bei Welchem später eine Instanz mir der entgültigen Funktion (INI oder Registry) registriert wird und an das alle Zugriffe weitergeleitet werden.


Wenn ich eine Funktion
Delphi-Quellcode:
procedure LoadData(data: TStream);
habe, dann kann ich der geben was ich will, aber ich komm doch nicht auf die Idee in den Sourcen die Unit auszutauschen, wo TStream implementiert ist und dafür "hart" ein TStream (mit der Funktion von TFileStream) einzubauen oder eben ein TStream (mit der Funktion fon TMemoryStream) oder ....

Codehunter 19. Okt 2012 18:53

AW: Objektorientiertes TIniFile-Replacement mit Unicode-Fähigkeit
 
@Popov:

Ich hab mir auch mal EasyIniAppExe.pas angeschaut. Du wirst lachen, ich habe lange Zeit etwas ganz ähnliches benutzt. Im Prinzip ist deine Lösung recht einfach und macht den Code im Hauptprogramm ähnlich schlank wie meine Lösung. Der große Unterschied dürfte meiner Ansicht nach darin liegen, daß bei deinem Code bei jedem Lese- und Schreibzugriff eine neue Instanz TIniFile erzeugt wird. Wenn die Konfiguration recht umfangreich ist dürfte das irgendwann zu Dateikuddelmuddel führen, Stichwort Schreibcache.

Ich wollte vorallem eine Konfiguration, die ausschließlich im Speicher vorgehalten wird und nur bei einem UpdateFile oder SaveToFile wieder in strukturierte Textdaten umgewandelt wird.

@Himitsu: Danke für den Hinweis mit den lokalen Spracheinstellungen. Das halte ich auch für wichtig. Ich habe schlichtweg nicht daran gedacht. TFormatSettings gibt man da einfach als zusätzlichen Parameter an, sind overloaded Funktionen.

Das mit den codierten Zeilenumbrüchen ist vollkommen richtig, war mir schon bewusst, aber noch nicht dazu gekommen.

Mir ging es bei der Unit bzw. Klasse vorallem um das Prinzip, eine völlig eigenständige Implementierung von TIniFile mit neuem Designkonzept. Schließlich ist die originale Implementierung vom Ansatz her seit Delphi 1 nicht großartig verändert worden.

Man stelle sich ein Programm vor, das ein paar Hundert oder Tausend Settings speichert und lädt. Das ist mit meinem Konzept, denke ich, effizienter und man kann den Zeitpunkt des Dateischreibens auf Platte steuern, was mit der Unit von Popov nicht geht.

Mir ist auch irgendwann mal aufgefallen, das selbst bei einer einzigen TIniFile-Instanz bei vielen Random-Read-Write-Zugriffen irgendwann der Speicherinhalt durcheinander kam wenn man zwischenzeitlich ein Flush gemacht hat. Das ist mir bei meiner Unit noch nicht passiert.

himitsu 19. Okt 2012 19:16

AW: Objektorientiertes TIniFile-Replacement mit Unicode-Fähigkeit
 
Das mit der neuen Instanz von TIniFile macht eigentlich keine großen Probleme.
Vorallem da der Speichermanager in Delphi (und vorallem FastMM seit Delphi 2006) das kompensiert.
Aber der Hauptgrund ist, daß die INI-API von Windows etwas unpracktisch ist, da sie bei jedem einezelnem Zugriff (Lesen oder Schreiben) die Datei komplett neu einlies und parst und dann wieder freigibt, da kommt es auf die Objektverwaltung von delphi nicht wirklich drauf an. :angle2:

Popov 19. Okt 2012 19:35

AW: Objektorientiertes TIniFile-Replacement mit Unicode-Fähigkeit
 
@Codehunter

Meine Ini war/ist auch nie dazu gedacht gewesen bei Massenzugriffen tätig zu sein. Es spricht zwar nichts dagegen ein Objekt in einer Schleife paar hundert mal zu erstellen und wieder freizugeben, bei TIniFile wird das aber mit einem Zugriff auf eine Datei kombiniert. Sollte auch nicht das Problem sein, aber wozu dann die Datei paar hundert mal auch öffnen und schließen. Für so was war das ja auch nicht gedacht.

Trotzdem, es geht hier eher um das Konzept wie man sich das alles leichter machen kann.

PS:

Du bist nicht zufällig der Codehunter aus Spotlight.de?

Codehunter 20. Okt 2012 20:26

AW: Objektorientiertes TIniFile-Replacement mit Unicode-Fähigkeit
 
Also mir ging es um meine Projekte, die konfigurationstechnisch recht umfangreich sind. Weniger die Usersettings als Daten, die temporär anfallen oder Persistenz über Programmsitzungen hinaus usw. Da kommen stellenweise sehr viele Random-Zugriffe zusammen. Selbst TRegistry scheint da zeitweise ins Straucheln zu kommen. Daher mein Ansatz, die INI komplett im RAM zu halten.

Das TUniObjectIni kann theoretisch (und sollte praktisch) als globale Instanz während der ganzen Runtime mitlaufen und nur z.B. alle 5 Minuten mal auf Platte schreiben. Was ich noch einbauen will ist Unterstützung für Namespaces innerhalb des Ini-Files und threaded Dateioperationen. Und natürlich die Vorschläge eurerseits.

Der Denkansatz ist in meinem Fall einfach komplett anders als bei der gewöhnlichen INI. Das Konzept wurde ja vor Urzeiten erfunden als PCs noch mit 640 kB RAM auskommen mussten und CPUs kaum ein Millionstel der heutigen Rechenleistung hatten. Insofern kann man heute mit dem RAM ein bisschen verschwenderischer sein und sich auch häufigere Multibyte-Stringvergleiche leisten. Ich wollte eben etwas komplett objektorientiertes. Der prozedurale Anteil ist ja eigentlich nur als Rückwärtskompatibilität zum alten TIniFile gedacht.

@Popov: Jo ich bin immernoch der selbe Codehunter wie in Spotlight. Bist du noch der selbe Popov? :)

Popov 20. Okt 2012 20:44

AW: Objektorientiertes TIniFile-Replacement mit Unicode-Fähigkeit
 
Das mit dem Halten im RAM ist eine gute Idee und erhöht die Performance je nachdem ungemein, es gibt aber auch Schattenseiten. So habe ich Programme bei denen ich im laufe der Sitzung vieles einstelle usw. Beende ich das Programm regulär, ist alle gespeichert, stürzt das Programm oder ich kille es über den TM, ist alles weg.

PS: Ja, es gibt nur einen Popov. Schön sich wiedergesehen zu haben ;) Schade um Spotlight.

Codehunter 20. Okt 2012 21:04

AW: Objektorientiertes TIniFile-Replacement mit Unicode-Fähigkeit
 
Über das Absturzproblem habe ich auch viel nachgedacht. Zumal ich hin und wieder wilde Experimente mit multithreaded Programmen mache, die grad am Anfang recht absturzfreudig sind. Aber sagen wir mal so: Im Vergleich zu TIniFile ist das nicht so grundlegend anders. Auch da hat der Windows-Schreibcache kackfrech alles verworfen was das zwischenzeitlich abgestürzte Programm noch hinterlassen hat. Wollte man sich da absichern, ist mir bisher nur eine halbwegs gute Lösung eingefallen: Den Teil mit der INI in einen Systemdienst verlegen und die ganzen Speichereien per Inter-Prozess-Kommunikation transportieren. Geht die Anwendung den Bach runter, dann kann der Dienst noch alles speichern. Aber machen wir uns nichts vor, dieser Weg dürfte performancetechnisch ein Krückstock sein.

Tja Spotlight, das war noch eine schöne Zeit. Irgendwie war das auch ein Relikt aus längst vergangenen Zeiten. Denn mir ist heute keine Plattform mehr bekannt, wo so ein bunt gewürfelter Haufen so locker und höflich miteinander umgehen täte. Wäre Peter damals gesundheitsmäßig nicht auf dem Zahnfleisch gegangen, dann wäre das wohl noch bis heute so. Aber schaltet man so eine Plattform einmal ab, dann erholt sich das nicht mehr. Was Delphi angeht hat die DP hier mehr als nur einen Ersatz geschaffen. Ich glaub Luckie war doch auch Spotlightianer bevor er die DP aufgezogen hat, oder? Aber was mir persönlich am meisten fehlt ist die Haustier- und Heimwerker-Abteilung gleich beim Delphiforum um die Ecke ;) Was mir auch noch gut in Erinnerung geblieben ist: Als Spotlight damals endgültig abgeschaltet wurde hatte ich 9.993 Posts auf dem Zähler. Peter hat damals glaub ich den 10.000er Club kleine Geschenkchen gemacht...


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:54 Uhr.
Seite 1 von 3  1 23      

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