Laden...

[INI] Eigens verfasste Bibilothek zum Lesen, Schreiben und Ändern von '.ini' Dateien

Erstellt von Sorpz vor 11 Jahren Letzter Beitrag vor 11 Jahren 7.422 Views
S
Sorpz Themenstarter:in
14 Beiträge seit 2012
vor 11 Jahren
[INI] Eigens verfasste Bibilothek zum Lesen, Schreiben und Ändern von '.ini' Dateien

Hallo zusammen,

ich habe mir vor einiger zeit die frage gestellt, ob es eine möglichkeit gibt ini dateien via programmcode sowohl zu schreiben, als auch zu lesen. Nachdem ich mich durch allerlei foren und opensource seiten hindurch gewühlt hatte, dachte ich mir ich könnte eine eigene klasse (bzw. bibilothek) schreiben, um eben diese dateien zu lesen und zu schreiben. Also habe ich mich daran gesetzt und ein wenig herrumgebastelt und ich muss sagen, dass ich von meiner eigenen leistung selber positiv überrascht war.

Meine bibilothek besteh im grunde aus zwei basisklassen. Zum einen die klasse IniDocument und zum anderen die klasse QuickIni wobei die zweite klasse auf dem code der ersten aufbaut und die handhabung erheblich erleichtert.

Nun zum aufbau der klassen, welcher in der klasse IniDocument wie folgt aussieht:

:rtfm:[Eigenschaften]

FilePath - Gibt den pfad der ini datei an

Sections - Besteht aus einer auflistung von sektionen zu welchen ich später noch komme

RegionSpace - Gibt an ob in der datei ein abstand zwischen den sektionen sein soll oder nicht

Text - Gibt den rohen text zurück mit welchem die klasse arbeitet

Um schoneinmal vorweg den aufbau in sektionen zu erläutern. Eine ini datei beinhaltet im normalfall verschiedene sektionen, welche mit unterschiedlichen eigenschaften (properties) gefüllt sind. Aus diesem grund habe ich die klasse IniSection implementiert auf deren aufbau ich später noch zu sprechen komme. Nun wieder zurück zum eigentlichen aufbau der basisklasse IniDocument. Diese beinhaltet selbstverständlich nicht nur eigenschaften...

:rtfm:[Methoden]

GetValue - Findet einen wert einer eigenschaft in einer vorhandenen sektion. Andernfalls gibt es eine fehlermeldung

RemoveProperty - Entfernt eine eigenschaft aus der einer anzugebenden sektion, falls vorhanden. Andernfalls passiert nichts

AddSection - Fügt eine sektion hinzu

RemoveSection [+1 überladung] - Entfernt eine section. Entweder via sektionsobjekt oder via name

FindSection - Findet eine sektion via name und gibt eine kopie davon zurück

Load - Lädt eine ini datei. Achtung: Alle nicht gespeicherten sachen gehen verloren

Save - Speichert die ini datei unter dem angegebenen pfad

Soweit zu den methoden. Ich habe auch hier eine kleinigkeit zur verbesserung der handhabung eingesetzt -> den indexer. Wer nichts mit dem begriff anfangen kann hier die msdn :rtfm:.
Der indexer bietet die möglichkeit einfach und schnell auf eigenschaften zuzugreifen und sie zu verändern oder auszulesen.

Um nun die möglicherweise aufgekommene frage "warum zwei klassen?" zu beantworten beschreibe ich an dieser stelle einmal die unterschiede. Bei der klasse IniDocument brauch man ein wenig zeit um das ganze zu erstellen. Benutzt man allerdings die QuickIni klasse kann man sich eine menge zeit und arbeit sparen. Allerdings verzichtet diese auf kleinere tweaks wie z.B. #RegionSpace.
Benutzt man also die erstere klasse, muss man sich ein bisschen mehr zeit und arbeit einplanen. Ein grund dafür ist mit die notwendinge erstellung eigener sektionsobjekte. Bei der QuickIni klasse wird darauf verzichtet, hier geschieht dies alles automatisch.

Damit komme ich auch schon allmälig zum ende. Nur noch ein kleineres "add-on" ist zu erwähnen. Die möglichkeit die foreach-schleife zu verwenden:


foreach (IniSection sec in myIniFile)
{
// [...] benutzerdefinierter code
}

Ebenfalls in auf IniSection klassenobjekte anwendbar:


foreach (string prop in myIniSection)
{
// [...] benutzerdefinierter code
}

Soweit sogut. Ich hoffe ich konnte mit diesem projekt ein paar von euch helfen. Bei fragen oder möglicherweise aufgetretenen fehlern sowie verbesserungsvorschlägen stehe ich gerne zur verfügung und wäre sogar dankbar.
Danke fürs lesen. Hier noch der anhang... mfg jan

16.827 Beiträge seit 2008
vor 11 Jahren

Hi,

danke für die Bereitstellung Deiner Ini.

Ich hab mir das Recht mal rausgenommen mir das anzuschauen.. und hab da ein paar Anmerkungen 😉

Auch wenn es gegen die INI-"Regeln" verstößt, so ist es Möglich, dass ein Entwickler identische Sections innerhalb einer INI anlegt (hatte den Fall erst vor kurzem). Das ist wirklich nicht selten und oft auch nicht mehr anders zu lösen (Fremdkomponenten, die einfach Einträge hinzusetzen etc).
Du wirfst hier (IniDocument) leider eine Exception ( leider eine Allgemeine, und keine spezifische ), sofern der Name identisch ist.
Hier könnte man aus Kompatbilitätsgründen auch eine Zusammenfassung der Sections machen - und erst einen Fehler werfen, wenn identische Keys verwendet sein sollten.
Wobei es laut Wiki auch möglich ist, dass daraus ein Array wird, oder der zweite Wert einfach ignoriert wird.
Vielleicht bietest Du einfach einen zusätzlichen Parameter an (Enum), der die Auswahl einer strikten und einer verzeihbaren Durchführung zulässt.

Schön wäre es auch, wenn man quasi ein IniDocument mit einem Pfad anlegen kann; hier wirfst Du aber eine FileNotFound-Exception. Mir bleibt hier also nur der Konstruktor ohne Pfadangabe. Da aber die Save-Methode ebenfalls keinen Pfad entgegen nimmt müsste man hier den umständlichen Weg über die FilePath-Property gehen.
Du könntest Dich auch an XDocument orientieren, das für das Laden bestehender XML-Dateien eine statische Load-Methode anbietet.

Ebenso denke ich - wenn Du schon zwei Streams innerhalb der Klasse als privates Feld deklarierst, solltest Du dem Entwickler auch eine IDisposable-Schnittstelle anbieten, um das alles in ein using packen zu können. Kommt es zu einem Fehler in der Load-Methode, so bleibt Dein Stream einfach offen.
Da Du die Streams aber sowieso nur für einzelne Dinge verwendest - wie das Save() - müsste man die Writer auch gar nicht als Klassen-Feld deklarieren. Hier könntest Du selbst mit usings arbeiten (sofern Du das nicht tust - 1:1 ist der Code hier ja nicht).

Peformanter wäre es, wenn Du mit Dictionaries statt mit Listen, die Du ständig durchsuchst, arbeiten würdest. Oder Du bietest einen eigenes Object an (IniKeyValuePair), das Du dann mit Linq sehr performant suchen könntest.
Oder Du arbeitest Direkt mit Concurrent-Collections und machst das alles Thread-Safe ((wovon ich ein wirklicher Freund bin 😃) - ich basteln alles nach, was nicht Thread-Safe ist und ich benötige; Webanwendungen eben)

Wünschenswert wären auch generische Rückgaben; dass Du zum Beispiel die Value eines Keys erhälst und über eine Typübergabe direkt den korrekten / zu erwartenden Rückgabetyp erhälst. zB eben ein True direkt als Bool castest.
public T GetValue<T>(string key)

Trotzdem saubere Arbeit und danke für das Bereitstellen!

S
Sorpz Themenstarter:in
14 Beiträge seit 2012
vor 11 Jahren

Hey,

großes danke erstmal, dass du dir die zeit genommen und dir mein projekt angesehen hast. Ich hoffe das es ein wenig verständnis dafür gibt, dass ich mit manchen sachen noch nicht all zu viel anfangen kann. Ich mache das ganze zwar auch in der schule, allerdings bin ich erst 16 jahre alt und in der schule versucht man immernoch uns schleifen bei zu bringen 😄.

Auf einige, bzw eigentlich alle deiner vorschläge werde ich versuchen einzugehen, da ich die ganzen ideen als sehr einleutend und sinvoll finde.

Zum schluss noch eine frage.
Wenn ich die :::

Nochmal dankesehr für das tolle feedback... mfg jan

5.658 Beiträge seit 2006
vor 11 Jahren

Ich mache das ganze zwar auch in der schule, allerdings bin ich erst 16 jahre alt und in der schule versucht man immernoch uns schleifen bei zu bringen 😄.

...aber offenbar keine Rechtschreibung! SCNR

Was würde passieren, wenn man versuchen würde diesen per :::

Das geht in Richtung Serialisierung, d.h. den Wert eines (beliebigen) Datentyp abzuspeichern und wieder zu laden. Dafür werden üblicherweise XML- oder Binärdateien verwendet, aber keine Textdateien. Wenn du es doch so versuchen willst, dann mußt du eine Möglichkeit finden, Objekte in Text und wieder zurück umzuwandeln. Mir fällt da spontan ein, das Objekt in Binärdaten zu serialisieren und dann als BASE64 kodiert in die INI-Datei zu schreiben. Ein Objekt wie einen Socket zu serialisieren, ergibt aber tatsächlich keinen Sinn.

Christian

Weeks of programming can save you hours of planning

16.827 Beiträge seit 2008
vor 11 Jahren

An für sich "musst" Du nur Werttypen unterstützen.
Wenn er nen anderen Typ angibt hat er einfach pech gehabt; die Exception würde ich an Deiner Stelle nicht mal behandeln, sondern ungefiltert weiter reichen.

Ein Snippet hierfür wäre (ausm Kopf):

        public T GetValue<T>(  String key )
        {
            return ( T ) Convert.ChangeType( GetValue( key ), typeof( T ) );
        }
5.658 Beiträge seit 2006
vor 11 Jahren

Kleiner Verbesserungsvorschlag für die Methodensignatur:

public T GetValue<T>(String key) where T : struct

Damit hat man es gleich auf die Verwendung von Werttypen eingeschränkt.
Christian

Weeks of programming can save you hours of planning

16.827 Beiträge seit 2008
vor 11 Jahren

Kleiner Verbesserungsvorschlag für die Methodensignatur

Ich dachte mir gerade "guter Einwand"; und geschaut ob ich das auch so hab - tatsächlich nicht. Aber ich weiß auch wieder wieso:

Fehlermeldung:
Der Typ "string" darf keine NULL-Werte zulassen, wenn er als 'T'-Parameter im generischen Typ oder in der generischen Methode "...........GetAttributeValue<T>(System.Xml.Linq.XElement, string)" verwendet werden soll.

Klar könnte ma jetzt überaus kompliziert umgehen - und String ist auch kein Werttyp; aber steht in meinen Augen nicht im Verhältnis zum Nutzen 😉
Wir sollten hier aber keine fachlichen Theme besprechen sondern nur Verbesserungsvorschläge behandeln.

5.658 Beiträge seit 2006
vor 11 Jahren

Hi Abt,

hätte man nicht sowieso eine GetValue-Methode, die einen String zurückgibt. Also die, die dann aus der generischen Methode aus aufgerufen wird, und den Text aus der Ini-Datei zurückgibt?


public string GetValue(String key) 

und

public T GetValue<T>(String key) where T : struct

Christian

Weeks of programming can save you hours of planning

Hinweis von herbivore vor 11 Jahren

Bitte achtet darauf, dass ihr euch hier auf Verbesserungsvorschläge beschränkt. Bei Problemen mit der Umsetzung gehören entsprechend Fragen in das passende Entwicklung Forum.

J
130 Beiträge seit 2008
vor 11 Jahren

Hi Sorpz,

danke für dein Posting. Ich verwende eigentlich nur noch app.configs.
Ich denke Inis werden eine immer unwichtigere Rolle spielen. Höchstens wenn eine Kompatiblität zu ältere Software erhalten bleiben muss etc. Für Neuentwicklungen halte ich Inis für zu alt.

Nur meine Meinung.

Keep up the good work,
Cheers,
Jimpi

**“DOH !” -Homer Simpson**