Inhaltsverzeichnis

std.ini

INI-Konfigurationen lesen und schreiben: Sektionen ([server]), Schlüssel-Wert-Zeilen, Kommentare mit # und ;, typisierte Abfragen mit Vorgabewert, Iteration über Sektionen und Schlüssel, Laden und Speichern.

→ std.string · std.fs · Standard Library

Alle Beispiele dieser Seite sind mit lyxc 1.0.21A übersetzt und ausgeführt; die gezeigten Ausgaben sind echte Programmausgaben.

Einsatz: Anwendungs- und Dienstkonfiguration, Embedded-Systeme, Migrationsskripte — überall dort, wo eine lesbare Schlüssel-Wert-Datei ohne XML- oder JSON-Aufwand genügt.


Wie das Dokument aussieht

Das „Dokument-Handle„ ist eine Kopie des INI-Textes im Speicher, kein Datenmodell. ParseString kopiert die Eingabe, alle weiteren Funktionen durchsuchen diesen Text bei jedem Aufruf von vorn.

Das hat zwei angenehme Folgen:

Und eine unangenehme:

 
Der Dokumentpuffer ist genau so groß wie der Ausgangstext. ParseString legt alloc(len + 1) an; die Änderungsfunktionen schreiben ihr Ergebnis anschließend in denselben Puffer zurück. Wird der Text dabei länger — bei jedem neuen Schlüssel, jeder neuen Sektion, jedem längeren Wert — schreibt die Unit über das Pufferende hinaus in fremden Speicher (#1426).

Nachgemessen: ein Dokument aus [a]\nk=1\n (9 Byte Puffer) enthält nach zwei SetString-Aufrufen 108 Zeichen, der Nachbarblock ist überschrieben.

Bis das behoben ist: Entweder nur lesen, oder den Ausgangstext von vornherein groß genug wählen — etwa indem alle Schlüssel bereits mit Platzhalterwerten in der Datei stehen und SetString sie nur ersetzt (gleich lang oder kürzer).

Lesen

Signatur Beschreibung
ParseString(input: pchar): int64 Kopiert den Text und liefert das Handle; 0 bei leerer Eingabe
LoadFile(path: pchar): int64 Datei einlesen und Handle liefern
GetString(doc, section, key, default_val: pchar): pchar Wert oder Vorgabe
GetInt(doc, section, key, default_val: int64): int64 wie oben, als Ganzzahl
GetBool(doc, section, key, default_val: bool): bool true/1/yes/on gelten als wahr
GetFloat(doc, section, key, default_val: f64): f64 als Fließkommazahl
HasSection(doc, section): bool · HasKey(doc, section, key): bool Bestandsprüfung
GetSectionCount(doc): int64 · GetKeyCount(doc, section): int64 Anzahl

Werte werden getrimmt, und alles ab dem ersten # oder ; gilt als Kommentar — auch mitten in der Zeile.

import std.ini;
import std.alloc;
import std.string;

fn J(b: bool): pchar { if (b) { return "ja"; } return "nein"; }

fn main(): int64 {
    var text: pchar := "; Konfiguration\n[server]\nhost = example.org   ; Kommentar am Zeilenende\nport=8080\ndebug = true\nfaktor = 1.5\n\n[leer]\n"c;
    var doc: int64 := ParseString(text);

    // Typisierte Getter, jeweils mit Vorgabewert
    PrintLn(StrConcat("host:    ", GetString(doc, "server"c, "host"c, "?"c)));
    PrintLn(StrConcat("port:    ", IntToStr(GetInt(doc, "server"c, "port"c, 0))));
    PrintLn(StrConcat("debug:   ", J(GetBool(doc, "server"c, "debug"c, false))));
    PrintLn(StrConcat("fehlt:   ", GetString(doc, "server"c, "gibtsnicht"c, "Vorgabe"c)));
    PrintLn(StrConcat("Sektion fehlt: ", GetString(doc, "andere"c, "host"c, "Vorgabe"c)));

    // Bestand pruefen
    PrintLn(StrConcat("HasSection('server'):    ", J(HasSection(doc, "server"c))));
    PrintLn(StrConcat("HasKey('server','port'): ", J(HasKey(doc, "server"c, "port"c))));
    PrintLn(StrConcat("Sektionen insgesamt:     ", IntToStr(GetSectionCount(doc))));
    PrintLn(StrConcat("Schluessel in [server]:  ", IntToStr(GetKeyCount(doc, "server"c))));
    return 0;
}

host:    example.org
port:    8080
debug:   ja
fehlt:   Vorgabe
Sektion fehlt: Vorgabe
HasSection('server'):    ja
HasKey('server','port'): ja
Sektionen insgesamt:     2
Schluessel in [server]:  4

 
GetString legt für jeden Treffer einen neuen Puffer an — GetInt, GetBool und GetFloat rufen es intern auf und verhalten sich genauso. Zum Freigeben gibt es inzwischen FreeString(value) (#1428, nachgemessen mit lyxc 1.1.11B); die frühere Aussage „eine Freigabefunktion gibt es nicht“ gilt nicht mehr.

Für einen langlaufenden Dienst bleibt der bessere Weg trotzdem: Konfiguration einmal beim Start auslesen und die Werte selbst vorhalten, statt bei jedem Zugriff neu abzufragen und freizugeben.

Auflisten

Signatur Beschreibung
GetSections(doc, output: pchar, max_count: int64): int64 schreibt die Sektionsnamen, liefert deren Anzahl
GetKeys(doc, section, output: pchar, max_count: int64): int64 dasselbe für die Schlüssel einer Sektion

Ausgabeformat: Die Namen liegen dicht hintereinander, jeweils nullterminiert — kein festes Raster. Zum nächsten Namen kommt man über p + StrLen(p) + 1.

import std.ini;
import std.alloc;
import std.string;

// Sektions- und Schluesselnamen liegen dicht hintereinander, je nullterminiert
fn Naechster(p: int64): int64 { return p + StrLen(p as pchar) + 1; }

fn main(): int64 {
    // ACHTUNG: Der Dokumentpuffer ist nur so gross wie der Ausgangstext (#1426).
    // Deshalb hier von vornherein genug Text - oder gar nicht erst aendern.
    var doc: int64 := ParseString("[server]\nhost = example.org\nport = 8080\n\n[logging]\nlevel = info\n"c);

    var buf: int64 := alloc(4096);
    var i: int64 := 0;
    while (i < 4096) { poke8(buf + i, 0); i := i + 1; }

    var n: int64 := GetSections(doc, buf as pchar, 16);
    PrintLn(StrConcat("Sektionen: ", IntToStr(n)));
    var p: int64 := buf;
    i := 0;
    while (i < n) { PrintLn(StrConcat("  ", p as pchar)); p := Naechster(p); i := i + 1; }

    i := 0;
    while (i < 4096) { poke8(buf + i, 0); i := i + 1; }
    n := GetKeys(doc, "server"c, buf as pchar, 16);
    PrintLn(StrConcat("Schluessel in [server]: ", IntToStr(n)));
    p := buf; i := 0;
    while (i < n) { PrintLn(StrConcat("  ", p as pchar)); p := Naechster(p); i := i + 1; }

    // Zeilen von Hand bauen - fuer eigene Ausgabe ohne Set...
    var o: pchar := alloc(256) as pchar;
    BuildSectionHeader("db"c, o);       PrintLn(StrConcat("Kopfzeile:  ", o));
    BuildKeyValueLine("user"c, "lyx"c, o); PrintLn(StrConcat("Wertzeile:  ", o));

    // Namen pruefen, bevor sie geschrieben werden
    PrintLn(StrConcat("IsValidSectionName('a b'): ", IntToStr(IsValidSectionName("a b"c) as int64)));
    PrintLn(StrConcat("IsValidKeyName('po rt'):   ", IntToStr(IsValidKeyName("po rt"c) as int64)));
    return 0;
}

Sektionen: 2
  server
  logging
Schluessel in [server]: 2
  host
  port
Kopfzeile:  [db]
Wertzeile:  user=lyx
IsValidSectionName('a b'): 0
IsValidKeyName('po rt'):   0


Ändern und Speichern

Signatur Beschreibung
SetString(doc, section, key, value): void Setzt oder ersetzt; legt Sektion und Schlüssel bei Bedarf an
SetInt · SetBool · SetFloat Hüllen um SetString
DeleteKey(doc, section, key): bool · DeleteSection(doc, section): bool Entfernen
WriteString(doc, output, max_len): int64 Dokument in einen Puffer schreiben
WriteFile(doc, path): bool · SaveFile(doc, path): bool in eine Datei schreiben

Alle Set…-Funktionen geben nichts zurück — ob die Änderung gelungen ist, lässt sich nur über ein anschließendes HasKey oder GetString feststellen. Der Überlauf beim Wachsen ist behoben (#1426, nachgemessen mit lyxc 1.1.11B): ein Wert darf länger werden als der, den er ersetzt. ParseString weist außerdem ein Dokument ab, das INI_BUFFER_SIZE erreicht, und liefert dann 0.

 
WriteString schreibt nicht das Dokument, sondern einen festen Beispieltext (#1827, gemessen mit lyxc 1.1.11B). Unabhängig vom Inhalt von doc kommt immer dies heraus:

> # Generated by std.ini
> [Settings]
> key=value
> 


Der Rückgabewert ist die Länge dieses Texts, eine Fehlermeldung gibt es nicht. Zum Herausschreiben WriteFile oder SaveFile benutzen — die nehmen den echten Inhalt; ein Durchlauf LoadFile → SetString → WriteFile → LoadFile trägt den gesetzten Wert.

Die Löschfunktionen verkürzen den Text und sind deshalb unbedenklich; ein Durchlauf Laden → Löschen → Speichern erhält Kommentare und Reihenfolge:

; Kopfkommentar
[s]
k = 1   # Zeilenkommentar

bleibt nach LoadFile + SaveFile unverändert erhalten.


Zeilen und Namen von Hand

Signatur Beschreibung
BuildSectionHeader(section, output): int64 [name]
BuildKeyValueLine(key, value, output): int64 key=value
ParseLine(line, line_len, key_out, value_out): bool Zerlegt eine Schlüssel-Wert-Zeile
IsSectionLine(line, line_len): bool Ist die Zeile ein Sektionskopf?
ParseSectionLine(line, line_len, output): int64 Sektionsnamen aus der Kopfzeile
IsValidSectionName(name): bool · IsValidKeyName(key): bool Namen prüfen — Leerzeichen sind unzulässig
EscapeValue(input, output): int64 · UnescapeValue(input, output): int64 Sonderzeichen im Wert

Diese Bausteine erlauben es, eine INI-Datei zusammenzusetzen, ohne die Set…-Funktionen zu benutzen — der derzeit sicherste Weg, eine Datei neu zu schreiben.

 
EscapeValue maskiert ; und # (#1427, nachgemessen mit lyxc 1.2.2B): aus wert;mit#raute wird wert\\;mit\\#raute. Vorher gingen Werte mit Semikolon beim nächsten Lesen verloren, weil der eigene Parser dort abschneidet.

Konstanten

Konstante Wert Wirkung
MAX_SECTIONS 64 keine
MAX_ENTRIES_PER_SECTION 128 keine
MAX_KEY_LENGTH 64 keine
MAX_VALUE_LENGTH 256 keine
MAX_LINE_LENGTH 512 keine
INI_BUFFER_SIZE 65536 Größe des Zwischenpuffers beim Ändern
 
Die fünf Grenzwerte werden nirgends geprüft (#1429). Sie stammen aus einer früheren, tabellenbasierten Fassung; seit das Dokument roher Text ist, gibt es weder eine Sektionstabelle noch feste Feldlängen. Ein Schlüssel mit 70 Zeichen wird anstandslos gesetzt und gefunden, mehr als 64 Sektionen ebenso.

Die einzige wirksame Grenze ist INI_BUFFER_SIZE. Sie wird inzwischen geprüft: ParseString weist eine Eingabe ab, die 65 536 Zeichen erreicht, und liefert 0 statt eines Überlaufs (#1426).

Die fünf übrigen Werte bleiben wirkungslos — mit lyxc 1.1.11B nachgemessen: ein Schlüssel mit 81 Zeichen wird gesetzt und wiedergefunden.

Kommentare hinzufügen

 
AddSectionComment und AddKeyComment sind nicht umgesetzt und brechen ab. Beide melden seit lyxc 1.0.15G std.ini: AddSectionComment ist nicht umgesetzt (#1244) bzw. … AddKeyComment … und beenden das Programm. Vorher liefen sie still durch, ohne einen Kommentar zu schreiben — der Aufruf sah wie ein Erfolg aus.

Kommentare gehören also beim Zusammensetzen der Datei selbst gesetzt (BuildSectionHeader + eigene ; …-Zeilen). Beim Lesen und beim Roundtrip bleiben vorhandene Kommentare dagegen erhalten.

Fallstricke


Weiterführend: std.json · std.yaml · std.env · Standard Library

Quelle: std/ini.lyx (1069 Zeilen) · Autor: Andreas Röne · Copyright: 2024–2025 Andreas Röne

Letzte Aktualisierung: 2026-09-05 (#1427, nachgemessen mit lyxc 1.2.2B) — EscapeValue-Kasten auf den behobenen Stand gezogen.

Vorherige letzte Aktualisierung: 2026-08-13 — Seite gegen std/ini.lyx überarbeitet: zwei lauffähige Beispiele mit echter Ausgabe, das textbasierte Dokumentmodell und das Ausgabeformat von GetSections/GetKeys (dicht gepackt, nullterminiert) erstmals beschrieben, Fallstricke ergänzt. Belegt und als Issue erfasst: Dokumentpuffer wächst nicht mit (#1426), EscapeValue maskiert ;/# nicht (#1427), GetString leckt je Aufruf (#1428), die Grenzkonstanten sind wirkungslos (#1429). Geprüft mit lyxc 1.0.21A.

Letzte Aktualisierung: 2026-08-27 — Fallstricke gegen lyxc 1.1.11B nachgemessen: #1426 behoben (längere Werte, INI_BUFFER_SIZE wird geprüft), #1428 hat mit FreeString eine Freigabefunktion bekommen; #1427 (Semikolon im Wert) und #1429 (wirkungslose Grenzkonstanten) bestehen fort. Neu gefunden und gemeldet: WriteString schreibt einen festen Beispieltext (#1827).

Codebeispiele geprüft: gegen lyxc 1.2.5C übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die pub fn-Signaturen in aurum/std gehalten, 0 Abweichungen).