Dateibasiertes Konfigurationshandling in C (Unix)

Lesezeit: 10 Minuten

Benutzer-Avatar
Philipp Ekberg

Dies ist wahrscheinlich eine der häufigsten Aufgaben / Probleme beim Programmieren; Sie müssen die Konfiguration Ihrer Anwendung irgendwo speichern.

Während ich versuche, einen Webserver oder andere Anwendungen zu erstellen, möchte ich den Code so sauber wie möglich halten, da mein Hauptinteresse am Programmieren der Architektur gilt. Dies führt dazu, dass ich Konfigurationen in einer Datei speichern möchte, die geändert werden kann, ohne die Software neu kompilieren zu müssen.

Ich bin nicht hier, um das Rad neu zu erfinden oder so etwas, also würde ich gerne einen Konfigurationsleser in C auf *nix erstellen. Die Konfiguration kann der Konfiguration jeder anderen Software sehr ähnlich sein; Apache, vsftpd, MySQL usw.

Die grundlegende Frage ist: Wie liest man aus einer Textdatei und verarbeitet jede Zeile effizient (in reinem C)? Muss ich verwenden fgetc() und jedes Zeichen verarbeiten?

  • XML. Ick. Das ist eine Menge Overhead für eine einfache INI-Datei.

    – Charly Martin

    5. Januar 2009 um 0:09 Uhr

  • In der Tat mein Gedanke. Und es muss reines C sein und keine externen Bibliotheken verwenden. Also ich will lernen wie das geht 🙂

    – Philipp Ekberg

    5. Januar 2009 um 0:24 Uhr

Hmm gibt es LibConfig.

Ich habe es in gefunden Wiki

  • Nun, dies ist eine sehr schöne Bibliothek, die ich wahrscheinlich verwenden würde, wenn ich nichts über die zugrunde liegende Struktur erfahren möchte.

    – Philipp Ekberg

    5. Januar 2009 um 3:54 Uhr

Verschiedene Leute haben recht gute Ratschläge gegeben – das Pure-FTP-Beispiel ist interessant.

Sie sollten auch lesen TAOUP (Die Kunst der Unix-Programmierung) von ES Raymond. Es hat Beispiele für eine Fülle von Konfigurationsdateien. Es skizziert auch kanonische Redewendungen für die Konfigurationsdateien. Beispielsweise sollten Sie wahrscheinlich ‘#’ zulassen, um einen Kommentar am Ende der Zeile zu beginnen, und Leerzeilen ignorieren. Sie sollten auch entscheiden, was Sie tun, wenn die Konfigurationsdatei eine Zeile enthält, die Sie nicht verstehen – ob Sie ignorieren und stillschweigend fortfahren oder sich beschweren. (Ich bevorzuge Dinge, die sich beschweren; dann weiß ich, warum das, was ich gerade hinzugefügt habe, keine Wirkung hat – während stilles Ignorieren bedeutet, dass ich nicht weiß, ob der gerade hinzugefügte Eintrag eine Wirkung hat.)

Ein weiteres Problem ist das Auffinden der Konfigurationsdatei. Tun Sie das durch den einkompilierten Speicherort, durch einen Standardinstallationsspeicherort mit zu überschreibender Umgebungsvariable oder durch ein anderes Stück Magie? Stellen Sie sicher, dass es eine Befehlszeilenoption gibt, mit der die Konfigurationsdatei absolut angegeben werden kann – ziehen Sie sogar in Erwägung, dies als einzige Möglichkeit zu verwenden.

Ansonsten halten Sie es in weiten Grenzen einfach und alle sind glücklicher.

Benutzer-Avatar
Charly Martin

Okay, also kommen wir zum anderen Teil. Sie müssen darüber nachdenken, was Sie als Ihre “Sprache” haben möchten. In der UNIX-Welt ist die Art der kanonischen Version wahrscheinlich durch Leerzeichen getrennter Text (think /etc/hosts) oder “:” getrennter Text (wie /etc/passwd).

Sie haben ein paar Möglichkeiten, wobei die einfachste in gewisser Weise die Verwendung von scanf(3) ist. Lesen Sie erneut die Manpage für Details, aber wenn ein Zeileneintrag so etwas wie

port    100

dann suchst du sowas wie

char inbuf[MAXLINE];
int  val;

scanf("%s %d\n", &inbuf[0], &val);

Sie können etwas flexibler werden, wenn Sie einen einfachen FSA-Parse schreiben: Lesen Sie die Zeichen einzeln aus der Zeile und verwenden Sie einen endlichen Automaten, um zu definieren, was zu tun ist.

  • Dies ist anfällig für einen Pufferüberlauf, wenn das Feld länger als MAXLINE ist, richtig?

    – Blinken

    24. Mai 2014 um 12:15 Uhr

  • Ja. Und? Dies ist schließlich eine Diskussion über das Erstellen einer Konfigurationsdatei.

    – Charly Martin

    25. Mai 2014 um 1:17 Uhr

  • @CharlieMartin “Ja. Und? Dies ist schließlich eine Diskussion über das Erstellen einer Konfigurationsdatei.” uuund hier ist dein Exploit. Nahezu alle Konfigurationsdateien sind per Definition der Außenwelt ausgesetzt und können auf beliebige Weise von den Benutzern und anderen Programmen von Drittanbietern geändert werden. Es geht also nicht nur darum, “immerhin” eine Konfigurationsdatei zu erstellen. Siehe stackoverflow.com/questions/1621394/… für die Verwendung scanf richtig.

    – Bora M. Alper

    23. Juni 2017 um 7:41 Uhr


  • @boramalper Also, ja, du hast Recht, ich habe nicht über Pufferüberlauf-Schwachstellen gesprochen, während ich einem neuen Programmierer erklärte, wie man eine einfache Textdatei liest. Ich habe Endianness auch nicht erklärt. Oder Segmentierungsfehler. Mir ist oft aufgefallen, dass einer meiner Vorteile beim Programmieren darin besteht, dass ich mich noch daran erinnere, als ich noch nicht alles wusste.

    – Charly Martin

    24. Juni 2017 um 23:49 Uhr


  • @boramalper der Teil, in dem er fragt, ob er verwenden muss fgetc oder was.

    – Charly Martin

    25. Juni 2017 um 16:51 Uhr

Okay, hier ist ein echtes Beispiel für C-Code:

/* demo-fgets -- read a "demo.text", copy to stdout with line
   numbers. */

#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 100

FILE * fp;
char bufr[MAXLINE];

extern int errno ;

int main(int argc, char ** argv){
    int count = 0 ;
    if((fp = fopen("demo.text","r")) != NULL){
        /* then file opened successfully. */
        while(fgets(bufr,MAXLINE,fp)!=NULL){
            /* then no read error */
            count +=1;
            printf("%d: %s",     /* no "\n", why? */
                   count, bufr);
        }
        /* fgets returned null */
        if(errno != 0){
            perror(argv[0]);    /* argv[0]?  Why that? */
            exit(1);
        }
        exit(0);                /* EOF found, normal exit */
    } else {                    /* there was an error on open */
        perror(argv[0]);
        exit(1);
    }
}

Ich führe es mit dieser Eingabedatei aus:

520 $ cat demo.text 
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum
aliquet augue id quam. Sed a metus. Quisque sit amet quam. Sed id
ante. In egestas est non mi. Sed vel velit non elit vehicula
viverra. Curabitur eget tortor in ipsum vulputate
faucibus. Suspendisse imperdiet mauris at nibh. Sed interdum. Maecenas
vulputate, massa vel placerat mattis, ante est tincidunt sem, in
sollicitudin velit lacus non tortor. Etiam sagittis consequat nisl. 

Vestibulum id leo quis mauris gravida placerat. Donec aliquet justo a
tortor. Etiam nisi nibh, auctor non, luctus et, aliquam vitae,
metus. Cum sociis natoque penatibus et magnis dis parturient montes,
nascetur ridiculus mus. Nunc lacinia quam a ligula. Nulla quis nisi eu
nunc imperdiet cursus. Nunc vitae nisi vitae tellus posuere
sollicitudin. Nunc suscipit, dui ac interdum euismod, pede nisl varius
dui, sed mattis libero mauris eu felis. Nam mattis dui eget
nunc. Suspendisse malesuada, pede eget posuere pellentesque, neque
eros pretium nibh, ut blandit dui leo dapibus orci. Etiam lacinia
lectus at orci. Donec ligula lacus, sagittis nec, sodales et,
fringilla lobortis, eros. Etiam sit amet nulla. Aliquam mollis pede id
enim. Etiam ligula felis, pulvinar nec, vestibulum molestie, interdum
ut, urna. Ut porta ullamcorper diam. Nullam interdum arcu. 

Pellentesque habitant morbi tristique senectus et netus et malesuada
fames ac turpis egestas. Etiam eu enim quis sem accumsan
tristique. Proin non sem. Etiam quis ante. Aenean ornare pellentesque
dolor. Praesent sodales. Cras dui velit, scelerisque a, accumsan a,
vestibulum in, dui. Pellentesque sed sapien. Etiam augue est,
convallis eget, egestas vel, molestie id, turpis. Cum sociis natoque
penatibus et magnis dis parturient montes, nascetur ridiculus
mus. Cras posuere lorem eu diam. Ut ultricies velit. Nunc imperdiet
suscipit mauris. Vestibulum molestie elit id risus. Phasellus et
purus. Vestibulum id mauris. Fusce gravida elit quis turpis. Aliquam
ut est. 

Sed in mauris eu nulla rhoncus suscipit. Nam id dolor sit amet turpis
placerat sodales. Nunc ipsum. Quisque diam tellus, dapibus non,
interdum at, aliquam sit amet, tellus. Donec non pede eget massa
aliquam semper. Quisque dictum lacinia ipsum. Fusce magna purus,
mattis id, commodo et, lobortis eu, arcu. Vestibulum viverra neque a
nulla. Cum sociis natoque penatibus et magnis dis parturient montes,
nascetur ridiculus mus. Pellentesque vel felis in ligula blandit
auctor. Quisque quam. Curabitur turpis. Morbi molestie augue a
nisi. Nulla sollicitudin sagittis elit. Suspendisse in odio sed magna
dictum vestibulum. Duis facilisis lorem eget neque. Proin sit amet
urna eget velit scelerisque aliquam. Pellentesque imperdiet. Nullam
sapien. Nullam placerat ipsum eget metus. 

Mauris ornare risus eu velit. Morbi bibendum diam in sem. Morbi
aliquet nisl sit amet quam. Donec ornare sagittis nibh. Fusce ac
lectus. Sed sit amet risus. Integer facilisis commodo
sem. Pellentesque facilisis. Donec libero. Lorem ipsum dolor sit amet,
consectetur adipiscing elit.

Es gibt eine Reihe von Möglichkeiten. Du nicht müssen fgetc verwenden. Sie sollten wahrscheinlich die Manpage von stdio lesen, aber die kanonische Sache wäre, die Datei mit fopen(3) zu öffnen und dann mit fgets(3) zu lesen, um jeweils eine Zeile zu lesen. Das würde in etwa so aussehen:

#include <stdio.h>

FILE * fp ;
char bufr[MAXLINE];

if((fp = fopen(filename, "r") != NULL){
    while(! feof(fp)){
         fgets(bufr, MAXLINE, fp);
         /* Do stuff */
    }
} else {
    /* error processing, couldn't open file */
}

Könnte man sich auch anschauen libini bei Sourceforge.

  • Ah, ich verstehe, es ist also so, als würde man fgets in ASM verwenden (mit dem ich zuvor gearbeitet habe). War ein wenig neugierig, ob es möglich ist, es mit einem Dateistream zu verwenden. Danke, sehr geschätzt!

    – Philipp Ekberg

    5. Januar 2009 um 0:25 Uhr

  • Ich würde jedoch gerne wissen, wie man bis zur “nächsten Zeile” liest. Muss ich das selbst erstellen? Es gibt keine “readline”, die zu einem \n liest? oder zu einem anderen Token

    – Philipp Ekberg

    5. Januar 2009 um 0:26 Uhr

  • Ich bin jedoch mit der Antwort nicht ganz zufrieden, da sie nicht wirklich aussagt, wie ich die anderen effizient behandle 🙂

    – Philipp Ekberg

    5. Januar 2009 um 0:39 Uhr

  • fgets ist eine gelesene Zeile. Wenn Sie fgets ausführen, geht der “Dateizeiger” über die nächste neue Zeile hinaus. Die Schleife, die ich oben zeige, liest eine Datei Zeile für Zeile. Nur eine Sekunde und ich werde ein echtes Beispiel aufstellen.

    – Charly Martin

    5. Januar 2009 um 0:43 Uhr

  • Oh, verstehe, tut mir leid, dass ich das nicht verstanden habe! Sehr gute Antwort!

    – Philipp Ekberg

    5. Januar 2009 um 0:47 Uhr

Benutzer-Avatar
Ana Betts

Warum würdest du je Schreiben Sie diesen Code von Grund auf neu, es ist erledigt Also viele Male; Finden Sie einfach eine gute F/OSS-Implementierung und verwenden Sie diese.

Wie lesen Sie aus einer Textdatei und verarbeiten jede Zeile effizient

Machen Sie sich keine Sorgen um die Effizienz, es spielt keine Rolle für das Lesen von Konfigurationsdateien. Entscheiden Sie sich stattdessen für Einfachheit und Wartbarkeit.

  • Ah, ich verstehe, es ist also so, als würde man fgets in ASM verwenden (mit dem ich zuvor gearbeitet habe). War ein wenig neugierig, ob es möglich ist, es mit einem Dateistream zu verwenden. Danke, sehr geschätzt!

    – Philipp Ekberg

    5. Januar 2009 um 0:25 Uhr

  • Ich würde jedoch gerne wissen, wie man bis zur “nächsten Zeile” liest. Muss ich das selbst erstellen? Es gibt keine “readline”, die zu einem \n liest? oder zu einem anderen Token

    – Philipp Ekberg

    5. Januar 2009 um 0:26 Uhr

  • Ich bin jedoch mit der Antwort nicht ganz zufrieden, da sie nicht wirklich aussagt, wie ich die anderen effizient behandle 🙂

    – Philipp Ekberg

    5. Januar 2009 um 0:39 Uhr

  • fgets ist eine gelesene Zeile. Wenn Sie fgets ausführen, geht der “Dateizeiger” über die nächste neue Zeile hinaus. Die Schleife, die ich oben zeige, liest eine Datei Zeile für Zeile. Nur eine Sekunde und ich werde ein echtes Beispiel aufstellen.

    – Charly Martin

    5. Januar 2009 um 0:43 Uhr

  • Oh, verstehe, tut mir leid, dass ich das nicht verstanden habe! Sehr gute Antwort!

    – Philipp Ekberg

    5. Januar 2009 um 0:47 Uhr

Benutzer-Avatar
Vieldenker

So hätte ich es gemacht (Pseudo-Code):

while(there is input) {
  get one line;
  if (it is empty line || it beings with spaces followed by a '#') {
    skip this line, either empty or it's a comment;
  }
  find the position of the token that splits option name and its value;
  copy the option name and its value to separate variables;
  removing spaces before and after these variables if necessary;
  if (option == option1) {
     parse value for option 1;
  } else if (option == option2) {
     parse value for option 2;
  } else {
     handle unknown option name;
  }
  check consistency of options if necessary;
}

Beachten Sie, dass ein Teil des Codes (Prüfung auf leere Zeilen, Kommentare und Leerzeichen um Variablen) dazu da ist, Ihnen zusätzliche Flexibilität beim Schreiben der Konfigurationsdatei zu geben. Zum Beispiel werden Sie Ihr Programm nicht zum Absturz bringen, wenn Sie versehentlich hier und da ein zusätzliches Leerzeichen lassen.

Dies setzt voraus, dass Sie Konfigurationsdateien haben, die wie folgt aussehen:

# comment for option 1
option1 = value1

# comment for option 2
option2 = value2

...

1361520cookie-checkDateibasiertes Konfigurationshandling in C (Unix)

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy