Forum: PC-Programmierung Einstellungen im Programm speichern


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Max M. (maxi123456)


Lesenswert?

Was sind heute gängige Verfahren, um Einstellung in einem simplen 
Programm zu speichern (C++)?

InI Datei?
SQL Datenbank (z.B. eine kleine SQLite)?
Regedit? Was wäre das Pendant unter Linux?

Oder gibt es da inwzischen einfachere Lösungen?

von Harald K. (kirnbichler)


Lesenswert?

Was sind "Einstellungen"? Wie viele, welche Komplexität haben diese 
(sind das einfache numerische Werte, sind das komplexe Datenstrukturen 
o.ä.), wie fehlertolerant darf/muss das Abspeichern/Einlesen sein, 
welches Betriebssystem, welche Programmiersprache?

Sollen die "Einstellungen" auch transportiert werden können, oder 
sollen/dürfen die auf dem individuellen Rechner verbleiben?

von Dennis S. (eltio)


Lesenswert?

Eine Datenbank für (vermutlich) eine handvoll Einstellungen ist wohl 
etwas over-the-top. Aber es gibt standardisierte Formate wie XML, YAML 
oder JSON die weit verbreitet sind.

von Armin K. (-donald-) Benutzerseite


Lesenswert?

Ini mag oldstyle klingen, aber ich bin damit immer gut gefahren.

https://stackoverflow.com/questions/217902/reading-writing-an-ini-file

von Daniel A. (daniel-a)


Lesenswert?

Max M. schrieb:
> Regedit? Was wäre das Pendant unter Linux?

Das währe dann wohl gsettings/dconf (ehemals gconf). Ist aber eine reine 
Gnome Geschichte, und nur für Benutzer-/Desktop- Anwendungen. Nicht alle 
mögen es, die meisten Programme schreiben ihre Sachen einfach als File 
in XDG_CONFIG_HOME 
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html 
(Der Default ist normalerweise ~/.config/ für die User Settings und 
/etc/xdg/ Systemweit) (Dort gibt es übrigens auch das dconf 
Verzeichnis).

: Bearbeitet durch User
von Michael K. (brutzel)


Lesenswert?

Max M. schrieb:
> Was sind heute gängige Verfahren, um Einstellung in einem simplen
> Programm zu speichern (C++)?

JSON-Dateien sind ganz vorne dabei, würde ich sagen. XML soll ja schon 
wieder tot sein ... ;)

> InI Datei?
Kann man machen, ist aber manchmal dann doch zu einfach gestrickt.

> SQL Datenbank (z.B. eine kleine SQLite)?
Kanonen -> Spatzen.

> Regedit?
Würde ich eher nicht machen (es sei denn, es existieren sehr spezielle 
Gründe dafür).
- Geht nicht unter Linux.
- Die Einstellungen können nicht einfach kopiert werden.
- Leichen können in der Registry verbleiben.
- ...

Schau dir das mal an:
https://github.com/nlohmann/json

Ist plattformunabhängig und man muss nur eine Headerdatei einbinden.

> Oder gibt es da inwzischen einfachere Lösungen?

Selber basteln würde ich das jedenfalls nicht mehr. Es muss ja nicht nur 
irgendwie funktionieren, sondern sollte auch möglichst einfach und 
elegant zu verwenden sein. Die Werte, die man speichern und einlesen 
will, haben meist unterschiedliche Typen, was sich mit einer streng 
typisierten Sprache u.U. etwas beißen kann ... Und dies ist nur eines 
von mehreren Problemen, um die man sich kümmern muss. Das ist schon ein 
kleines Projekt für sich, wenn man es vernünftig machen will.

von Harald K. (kirnbichler)


Lesenswert?

Michael K. schrieb:
> Ist plattformunabhängig und man muss nur eine Headerdatei einbinden.

Man sollte sich allerdings Gedanken darum machen, wo man die Datei 
unterbringt. Das unterscheidet sich von Betriebssystem zu Betriebssystem 
doch erheblich.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Die einfachste Lösung dürfte auch die älteste sein, der Inhalt des 
relevanten Speicherbereichs wird binär so wie er ist in eine Datei 
geschrieben, die beim Programmstart wieder in den Speicher geladen 
werden kann.

Den ganzen anderen Quatsch braucht man nur wenn es für den Menschen 
einfach lesbar sein soll oder man ein bestimmtes Dateiformat erzeugen 
möchte, das möglicherweise kleiner wird als der abzubildende 
Speicherbereich, etwa weil nicht alle Variablen eines Programms 
gespeichert werden sollen.

Bzw. eine Initialisierungsdatei wie
1
Schalter1=an
2
Schalter2=aus
3
Schalter3=an
4
Schalter4=aus
5
Schalter5=an
6
Schalter6=aus
7
Schalter7=an
8
Schalter8=aus
hat für einen Computer genau so viel
Informations-Gehalt wie ein einzelnes Byte.

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Ben B. schrieb:
> Die einfachste Lösung dürfte auch die älteste sein, der Inhalt des
> relevanten Speicherbereichs wird binär so wie er ist in eine Datei
> geschrieben, die beim Programmstart wieder in den Speicher geladen
> werden kann.

Funktioniert halt nur, wenn der "relevante Speicherbereich" so 
strukturiert ist, daß er tatsächlich aus einem zusammenhängenden Block 
besteht. Und hat den schönen Reiz, daß es knallt, falls die Struktur des 
Speicherbereichs durch eine geänderte Programmversion verändert wird.

Ohne Versionierung ist so eine Binärdatei also eine leckere 
Fehlerquelle.

von Peter D. (peda)


Lesenswert?

Ben B. schrieb:
> Den ganzen anderen Quatsch braucht man nur wenn es für den Menschen
> einfach lesbar sein soll

Wenn da irgendein Mumpitz drinsteht, der die Anwendung gnadenlos 
abstürzen läßt, dann ist man doch heilfroh, wenn man es editieren kann.
Binärdaten hat man vielleicht benutzt, als die Programme noch auf eine 
160kB 5,25" Diskette passen mußten.

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

Zuerst wäre zu klären WO die Preferenzen gesichert werde sollen. Jedes 
OS hat dafür m.W einen speziellen Systemordner, sowas wie 
system.specialfolder.preferences (global) oder userspezifisch. Dann 
gibts noch die Möglichgkeit, die Daten bei der Anwendung (im selben 
Ordner) zu sichern. Das ist sinnvoll, wenn die Anwendung nicht speziell 
"installiert" werden muss, sondern portable ist.

Ich habe mir für Xojo eine Preferences-Klasse selber gemacht, die kann 
Integer, Double, String, Boolean und Color sichern und wieder laden. Der 
Speicherort und der Name der Datei werden im Constructor übergeben.

Beim Sichern gehe ich ungefähr so vor:

mypref.addStr("dbname", dbname.text)
mypref.save (ganz am Ende)

also Key-Value-Prinzip.

Beim Laden entsprechend umgekehrt:

dbname.text = mypref.getStr("dbname")

Verschlüsseln tu' ich es nicht, aber mit Base64 gegen Encoding-Fehler 
und all zu große Offensichtlichkeiten verpacken.

...

: Bearbeitet durch User
von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Er wollte eine möglichst einfache Möglichkeit wissen. Nichts für mehrere 
verschiedene Programmversionen oder sonstige Übertragbarkeit.

> Wenn da irgendein Mumpitz drinsteht,
> der die Anwendung gnadenlos abstürzen läßt [..]
Das ist kein Argument. Das kann bei einer .ini-Zeile wie 
"Zahl=hahalustig" genau so passieren wenn das Programm nicht auf solche 
Spaßvögel vorbereitet ist. Vielleicht kannst Du so einen Fehler noch 
beheben weil Du Dich für die Programmierung interessierst und solche 
Dateien lesen kann. Der 0815-Dau weiß aber nicht mal was eine .ini ist 
und kommt gar nicht erst auf die Idee, sich das mal genauer anzuschauen.

von Harald K. (kirnbichler)


Lesenswert?

Ben B. schrieb:
> Das ist kein Argument. Das kann bei einer .ini-Zeile wie
> "Zahl=hahalustig" genau so passieren wenn das Programm nicht auf solche
> Spaßvögel vorbereitet ist.

Nö. Ini-Dateien werden (sofern man die dafür vorgesehenen API-Funktionen 
verwendet) gezielt nach Key-Value-Paaren abgefragt. Wenn da zusätzliches 
Geraffel drinsteht, stört das nicht, denn der wird gar nicht erst 
gelesen.

Ein
1
int bla;
2
3
bla = GetPrivateProfileInt("Meinkram", "Meinwert", 0, meineinidatei);

liest folgenden Inhalt:
1
  
2
  [Meinkram]
3
  Meinwert=123

Existiert die Datei nicht, steht in der Datei kein Abschnitt "Meinkram" 
drin, gibt es keine Zeile "Meinwert=", gibt die Funktion den Wert 0 
zurück.

Damit kann man ein sicheres Defaultverhalten herbeiführen, man muss nur 
noch sicherstellen, daß der Wert insgesamt in einem sinnvollen 
Wertebereich liegt.

Das aber ist's dann auch schon.


Und gespeichert wird das ganze so:
1
int bla;
2
3
bla = 234;
4
5
WritePrivateProfileInt("Meinkram", "Meinwert", bla, meineinidatei);

Das einzige, worum man sich kümmern muss, ist das Bestimmen des in den 
Beispielen "meineinidatei" genannten Dateinamens und -Pfades.

Und man sollte einen Blick in die API-Dokumentation von MS reinwerfen.

: Bearbeitet durch User
von Motopick (motopick)


Angehängte Dateien:

Lesenswert?

Na, wer moechte das "leserlich" dekodieren?

Bonusfrage: Ist von welchem Programm?

von Stefan F. (Gast)


Lesenswert?

Michael K. schrieb:
> JSON-Dateien sind ganz vorne dabei, würde ich sagen.
> XML soll ja schon wieder tot sein

JSON auch. Der neue heiße Scheiß sind YAML Dateien.

Eine Datenbank würde ich nur verwenden, wenn das Programm aus anderen 
Gründen eh schon eine Datenbank hat. Ansonsten bin ich ein Freund von 
Text-basierten Dateien, die man manuell bearbeiten kann, wenn es mal 
sein muss.

von Max M. (maxi123456)


Lesenswert?

Harald K. schrieb:
> Was sind "Einstellungen"? Wie viele, welche Komplexität haben diese
> (sind das einfache numerische Werte, sind das komplexe Datenstrukturen
> o.ä.), wie fehlertolerant darf/muss das Abspeichern/Einlesen sein,
> welches Betriebssystem, welche Programmiersprache?
>
> Sollen die "Einstellungen" auch transportiert werden können, oder
> sollen/dürfen die auf dem individuellen Rechner verbleiben?

Nichts besonderes, das hätte ich ansonsten erwähnt. Vollkommener 
Standard, Zahlen, Strings. Parameter z.B. für eine CNC o.ä.

Michael K. schrieb:
> JSON-Dateien sind ganz vorne dabei, würde ich sagen. XML soll ja schon
> wieder tot sein ... ;)

JSON finde ich persönlich ganz schlimm.

Im Endeffekt JSON, XML, INI, binär (ganz sicher nicht).
Vermutlich werde ich es ganz altmodisch mit ini versuchen.

Wo speichern denn Programme wie z.B. KiCad ihre Parameter?

von Stefan F. (Gast)


Angehängte Dateien:

Lesenswert?

Max M. schrieb:
> Wo speichern denn Programme wie z.B. KiCad ihre Parameter?

Siehe Screenshot. Es sind überwiegend JSON Dateien, aber auch ein paar 
andere.

von Heinz B. (Firma: Privat) (hbrill)


Lesenswert?

Ich würde das auch in JSON machen, da es auch plattformübergreifend
(UTF-8) ist. Da gibt es bestimmt auch Libraries für C++ und Linux,
falls du mit Linux arbeitest.

Und viel schwerer, als mit einer .ini Datei ist es auch nicht.
Also für ein paar Einstellungen ideal. Dazu kommt noch, daß man
mit JSON Listen, deren Größe vorher noch unbestimmt ist, erzeugen
und erweitern kann.

{} = Objekt
[] = Liste

Für ein paar Einstellungen genügt da ein Haupt-Objekt und darin
die Werte :  "Schlüssel" : Wert|"Wert"|BOOL

Also ganz einfach :

{"Maschine" : "Stand1", "Datum" : "27.04.2023", "Startwert" : 100, 
"Parameter" : ["einString", 100, 0, 1]}

Wie man sieht, kann man in einer Liste auch Strings, Zahlen oder 
boolsche
Werte mischen.

Dafür braucht man ja keine .zig Objekt-Ebenen zum Durchhangeln. Und
wenn man mehrere Maschinen hat, muß man halt zuerst ein oberes Objekt
(z.B. Maschinen) erzeugen und darin halt die Unterobjekte.

Mittlerweile arbeite ich ganz gerne mit JSON.

PS: Übrigens kannst du das auch mit SQLITE3 (Vers. 3.38)  machen,
    wenn deine Sprache SQL-Statements (SQLEXEC) unterstützt :

    https://www.sqlite.org/json1.html

: Bearbeitet durch User
von Peter K. (Gast)


Lesenswert?

Back to the roots.
Die besten Zeiten waren die, wo man Programme einfach kopieren konnte, 
also alle Einstellungen  ebenfalls im gleichen Ordner wie das Programm 
lagen.
wenn es keinen zwingenden Grund gibt, weil z.B. ANDERE Programme von der 
Existenz deine Programms und womöglich dessen Optionen, wissen müssen, 
gibt es gar keinen Grund und finde es sogar extrem Kontraproduktiv, 
solche Informationen in irgendwelchen Registries abzulegen


Und wegen irgenwelcher Backup ist es ach ein wirkliches Argument

von Klaus P. (kpi6288)


Lesenswert?

Für Einstellungen bevorzuge ich nach wie vor XML.

Das ist auch plattformübergreifend und hat vor allem den Vorteil, dass 
man es einfach validieren kann. Mit XPath und XQuery sind auch einfache 
Abfragen möglich.

Außerdem geben viele Editoren direkt Hinweise, wenn die Datei formal 
nicht richtig ist, falls man die Daten einmal manuell korrigieren muss.

Dass es etwas größer ist als JSON, stört mich kaum. Die Dateien, um die 
es bei mir geht, sind meistens recht klein (maximal ein paar kB), nur 
wenige haben dann tatsächlich einmal mehrere MB. Ebenso kann ich gut 
damit leben, dass XPath Abfragen nicht besonders performant sind - der 
Komfortgewinn beim Programmieren gleicht das allemal aus; vor allem, 
wenn diese Abfragen uim Programm nicht häufig benutzt werden (wie es bei 
Konfigurationsdaten normalerweise der Fall ist).

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

> Nö. Ini-Dateien werden (sofern man die dafür vorgesehenen
> API-Funktionen verwendet) gezielt nach Key-Value-Paaren abgefragt.
> [..]
Das ist immer noch kein gutes Argument bzw. Du verlässt Dich halt auf 
eine vorgefertigte API oder ein anderes Interface (wie auch z.B. ein 
MySQL-Server eines wäre), was entsprechend gegen Vergewaltigung der 
gespeicherten Daten gehärtet ist. Das kann man natürlich machen, aber 
wenn man den Parser selbst schreibt oder ein eigenes Dateiformat 
verwendet, muss man sich auch selbst um solche Fehlerquellen kümmern und 
erlebt sehr merkwürdige Effekte, wenn man es nicht tut.

von Harald K. (kirnbichler)


Lesenswert?

Peter K. schrieb:
> Die besten Zeiten waren die, wo man Programme einfach kopieren konnte,
> also alle Einstellungen  ebenfalls im gleichen Ordner wie das Programm
> lagen.

Das waren die schlimmsten Zeiten. Da hat jeder Hinz und Kunz 
irgendwelchen Rotz irgendwo abgelegt, und man musste sich mühsam diesen 
ganzen Krempel zusammensuchen, wenn man es auf einen anderen Rechner 
übertragen wollte.

Das Konzept, alle von einem Benutzer erzeugten Dateien in einem 
Verzeichnisbaum unterzubringen, der dem Benutzer gehört, ist da deutlich 
besser -- dann können auch mehrere Leute mit einem Computer arbeiten, 
ohne durcheinanderzukommen.

Wenn die Lebensabschnittsbegleiterin gerne ihre Textdokumente in "Comic 
Sans" verfasst und die Standardformatvorlage so konfiguriert, muss man 
sich nicht drüber ärgern, denn man hat seine eigene 
Standardformatvorlage mit einer Schriftart für Erwachsene.

von Daniel A. (daniel-a)


Lesenswert?

Heinz B. schrieb:
> Ich würde das auch in JSON machen, da es auch plattformübergreifend
> (UTF-8) ist. Da gibt es bestimmt auch Libraries für C++ und Linux,
> falls du mit Linux arbeitest.

Mann muss dann einfach daran denken, dass man Binärdaten dann vorher 
z.B. base64 Kodiert / Dekodiert. Und in C kann man Strings mit 
beliebigen Byte folgen haben, z.B.
1
const char mymessage[] = "Hello\xFFWorld";
Wenn man das direkt als JSON String speichert, ist das \xFF kein 
gültiges Zeichen. Vielleicht ist der Encoder noch so clever, es 
wenigstens durch den Codepoint für ungültiges Zeichen zu ersetzen 
(\xEF\xBF\xBD) oder so, statt ungültiges JSON zu erstellen, aber das 
Zeichen so wieder Dekodieren wird nichts.

Ausserdem ist JSON eine Art Baumstruktur. Grafen, ala A.b=B B.a=A kann 
man darin nicht direkt abbilden, das muss man dann auch manuell anders 
darstellen.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Ja das ist auch so eine Unart, daß jeder sein eigenes Stringformat oder 
Zeichensatz bis UTF-65536 benutzt nur weil er denkt, das wäre cool. 
Deswegen bin ich bei meinen Datenbankprojekten schon lange dazu 
übergegangen, solchen Quatsch einfach komplett "escaped" wegzuspeichern. 
Wenn die search engine der Datenbank dann irgendwas nicht wiederfindet 
weil mit solchen Sonderzeichen rumgespielt werden musste - nicht mein 
Problem.

Ein Leerzeichen, wie man es für "Hello World!" benötigt, ist ASCII 32 - 
und nicht 255. Keine Ahnung, wieso man in normalem Fließtext 255 
benutzen sollte, außer man möchte das Programm ärgern.

von Daniel A. (daniel-a)


Lesenswert?

Die meisten Datenbanken kennen einen VARBINARY(N) Datentyp, analog zum 
VARCHAR(N). (Würde ich übrigens auch zum Speichern von Pfaden empfehlen, 
die müssen nämlich nicht unbedingt normalisiertes UTF-8 sein). Und 
analog zu TEXT gibt es BLOB.

In DBs escape ich nichts manuell. Da nimmt man die prepared Statements 
die die DB Library anbietet, und die passenden Datentypen für die Daten.

PS: Bei sqlite ist sowieso alles immer schnuppe. Datentypen, Foreign 
keys, etc. sind dort normalerweise nichts als blosse Suggestionen. Wobei 
man aber wenigstens ein paar Checks einschalten kann.

von Heinz B. (Firma: Privat) (hbrill)


Lesenswert?

ALT+255 hatte ich damals zu MS-DOS Zeiten zum Verstecken
von Dateien und dem DIR - Befehl benutzt.
Wozu das man heute noch braucht ?

von Εrnst B. (ernst)


Lesenswert?

Ben B. schrieb:
> Keine Ahnung, wieso man in normalem Fließtext 255
> benutzen sollte

Als Non-Breaking space, damit verhindert man Zeilenumbrüche im Fließtext 
 an Stellen an denen sie unerwünscht sind. Ist in UTF-8 allerdings nicht 
 255, sondern &#160.


Zum Thema zurück: Wenn der TE für sein C++ eine GUI-Bibliothek 
verwendet, sollte er mal nachsehen, ob die nicht schon was passendes 
mitbringt. z.B. https://doc.qt.io/qt-5/qsettings.html

Dann landen die Einstellungen auf jedem System in der dort üblichen Form 
am dort üblichen Platz...
https://doc.qt.io/qt-5/qsettings.html#platform-specific-notes

von Stefan F. (Gast)


Lesenswert?

Heinz B. schrieb:
> Also ganz einfach :
>
> {"Maschine" : "Stand1", "Datum" : "27.04.2023", "Startwert" : 100,
> "Parameter" : ["einString", 100, 0, 1]}

Das Selbe sieht in Yaml so aus:
1
Maschine: Stand1
2
Datum: 27.04.203
3
Startwert: 100
4
Parameter: [einString,100,0,1]

Oder alternativ so:
1
Maschine: Stand1
2
Datum: 27.04.203
3
Startwert: 100
4
Parameter: 
5
  - einString
6
  - 100
7
  - 0
8
  - 1

Im Gegensatz zu JSON kann man YAML Dateien kommentieren.

von Peter M. (r2d3)


Lesenswert?

Hallo allerseits,

Dennis S. schrieb:
> Eine Datenbank für (vermutlich) eine handvoll Einstellungen ist wohl
> etwas over-the-top.

Armin K. schrieb:
> Ini mag oldstyle klingen, aber ich bin damit immer gut gefahren.
>
> https://stackoverflow.com/questions/217902/reading-writing-an-ini-file

wenn ich diese Beiträge lese, fühle ich mich komplett out-of-date.

von C-hater (c-hater)


Lesenswert?

Harald K. schrieb:

> Das waren die schlimmsten Zeiten. Da hat jeder Hinz und Kunz
> irgendwelchen Rotz irgendwo abgelegt, und man musste sich mühsam diesen
> ganzen Krempel zusammensuchen, wenn man es auf einen anderen Rechner
> übertragen wollte.

Nein, so war das garnicht. Alles lag halt innerhalb (unterhalb) des 
Programmverzeichnisses.

Und es war sogar völlig problemlos möglich, benutzerspezifische 
Einstellungen auf diese Art abzulegen. Ist nur ein Alptraum bezüglich 
der Sicherheit und Vertraulichkeit. Bezüglich des reinen Datenflusses 
geht das aber völlig problemlos.

> Das Konzept, alle von einem Benutzer erzeugten Dateien in einem
> Verzeichnisbaum unterzubringen, der dem Benutzer gehört, ist da deutlich
> besser

Natürlich erheblich besser bezüglich Sicherheit und Vertraulichkeit. 
Aber führt auch einen Haufen Overhead ein. Da ist zum einen das Thema: 
"Einstellungen, die ein User ABSICHTLICH (in guter Absicht!) mit 
anderen Usern teilen möchte. Und zum anderen: Einstellungen, die eine 
"übergeordnete Autorität" dem User unumgehbar aufzwingen möchte.

Beides läßt sich mit dem Konzept der Home-Verzeichnisse nur schlecht bis 
garnicht abbilden.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.