it-swarm.com.de

Erstellen einer einfachen Konfigurationsdatei und eines Parsers in C++

Ich versuche, eine einfache Konfigurationsdatei zu erstellen, die so aussieht

url = http://mysite.com
file = main.exe
true = 0

wenn das Programm ausgeführt wird, möchte ich, dass die Konfigurationseinstellungen in die unten aufgeführten Programmvariablen geladen werden.

string url, file;
bool true_false;

Ich habe etwas recherchiert und dieser Link schien zu helfen (der Beitrag von Nucleon), aber ich kann nicht scheinen, dass es funktioniert, und es ist zu kompliziert, um es von meiner Seite zu verstehen. Gibt es eine einfache Möglichkeit, dies zu tun? Ich kann die Datei mit ifstream laden, aber soweit kann ich alleine damit umgehen. Vielen Dank!

45
llk

Im Allgemeinen ist es am einfachsten, solche typischen Konfigurationsdateien in zwei Schritten zu parsen: Lesen Sie zuerst die Zeilen und dann die Zeilen nacheinander.
In C++ können Zeilen mit std::getline() aus einem Stream gelesen werden. Während standardmäßig standardmäßig bis zum nächsten '\n' gelesen wird (den es konsumieren wird, aber nicht zurückgegeben wird), können Sie ihm auch ein anderes Trennzeichen übergeben, das es zu einem guten Kandidaten für das Lesen von Up-to-some-char macht, wie =. in deinem Beispiel. 

Der Einfachheit halber setzt das Folgende voraus, dass der = nicht von Leerzeichen umgeben ist. Wenn Sie Whitespaces an diesen Positionen zulassen möchten, müssen Sie is >> std::ws vor dem Lesen des Werts strategisch platzieren und nachfolgende Whitespaces aus den Schlüsseln entfernen. Für IMO ist die geringe zusätzliche Flexibilität in der Syntax für einen Konfigurationsdatei-Reader jedoch nicht den Aufwand wert. 

const char config[] = "url=http://example.com\n"
                      "file=main.exe\n"
                      "true=0";

std::istringstream is_file(config);

std::string line;
while( std::getline(is_file, line) )
{
  std::istringstream is_line(line);
  std::string key;
  if( std::getline(is_line, key, '=') )
  {
    std::string value;
    if( std::getline(is_line, value) ) 
      store_line(key, value);
  }
}

(Das Hinzufügen von Fehlerbehandlung wird dem Leser als Übung überlassen.)

42
sbi

Wie andere bereits erwähnt haben, wird es wahrscheinlich weniger Arbeit sein, eine vorhandene Parser-Bibliothek für Konfigurationsdateien zu verwenden, als das Rad neu zu erfinden.

Wenn Sie sich beispielsweise für die Config4Cpp -Bibliothek entscheiden (die ich behaupte), wird Ihre Konfigurationsdateisyntax etwas anders sein (setzen Sie die Werte in Anführungszeichen und beenden Sie die Zuweisungsanweisungen mit einem Semikolon), wie im folgenden Beispiel gezeigt :

# File: someFile.cfg
url = "http://mysite.com";
file = "main.exe";
true_false = "true";

Das folgende Programm analysiert die obige Konfigurationsdatei, kopiert die gewünschten Werte in Variablen und druckt sie:

#include <config4cpp/Configuration.h>
#include <iostream>
using namespace config4cpp;
using namespace std;

int main(int argc, char ** argv)
{
    Configuration *  cfg = Configuration::create();
    const char *     scope = "";
    const char *     configFile = "someFile.cfg";
    const char *     url;
    const char *     file;
    bool             true_false;

    try {
        cfg->parse(configFile);
        url        = cfg->lookupString(scope, "url");
        file       = cfg->lookupString(scope, "file");
        true_false = cfg->lookupBoolean(scope, "true_false");
    } catch(const ConfigurationException & ex) {
        cerr << ex.c_str() << endl;
        cfg->destroy();
        return 1;
    }
    cout << "url=" << url << "; file=" << file
         << "; true_false=" << true_false
         << endl;
    cfg->destroy();
    return 0;
}

Config4Cpp website bietet umfassende Dokumentation, aber das Lesen der Kapitel 2 und 3 des Handbuchs "Erste Schritte" sollte für Ihre Anforderungen mehr als ausreichend sein.

32
Ciaran McHale

Ein naiver Ansatz könnte so aussehen:

#include <map>
#include <sstream>
#include <stdexcept>
#include <string>

std::map<std::string, std::string> options; // global?

void parse(std::istream & cfgfile)
{
    for (std::string line; std::getline(cfgfile, line); )
    {
        std::istringstream iss(line);
        std::string id, eq, val;

        bool error = false;

        if (!(iss >> id))
        {
            error = true;
        }
        else if (id[0] == '#')
        {
            continue;
        }
        else if (!(iss >> eq >> val >> std::ws) || eq != "=" || iss.get() != EOF)
        {
            error = true;
        }

        if (error)
        {
            // do something appropriate: throw, skip, warn, etc.
        }
        else
        {
            options[id] = val;
        }
    }
}

Jetzt können Sie auf jeden Optionswert von der globalen options-Map an einer beliebigen Stelle in Ihrem Programm zugreifen. Wenn Sie die Umsetzbarkeit wünschen, können Sie den zugeordneten Typ als boost::variant festlegen.

13
Kerrek SB

libconfig ist sehr einfach, und was besser ist, es verwendet eine Pseudo-Json-Notation zur besseren Lesbarkeit.

Einfache Installation unter Ubuntu: Sudo apt-get install libconfig++8-dev

und link: -lconfig++

11
user1382306

Ich habe vor kurzem in Config-Parsing-Bibliotheken nach meinem Projekt gesucht und diese Bibliotheken gefunden:

3
Ivan Samygin

Wie wäre es, wenn Sie Ihre Konfiguration als JSON formatieren und eine Bibliothek wie jsoncpp verwenden? 

z.B.

{"url": "http://mysite dot com",
"file": "main.exe",
"true": 0}

Sie können es dann in benannte Variablen einlesen oder sogar in einer std :: map usw. speichern. Letzteres bedeutet, dass Sie Optionen hinzufügen können, ohne Ihren Konfigurationsparser ändern und neu kompilieren zu müssen.

3
patmanpato

Warum probieren Sie nicht einfach etwas und Menschen lesbares aus, wie JSON (oder XML)? 

Es gibt viele vorgefertigte Open-Source-Implementierungen von JSON (oder XML) für C++ - ich würde eine davon verwenden.

Und wenn Sie etwas mehr "Binär" wollen - versuchen Sie es mit BJSON oder BSON :)

3
yosh kemu

Hier ist eine einfache Lösung für den Leerraum zwischen dem Zeichen '=' und den Daten in der Konfigurationsdatei. Weisen Sie dem Istringstrom von der Position nach dem "=" - Zeichen zu. Wenn Sie von dort lesen, werden alle führenden Leerzeichen ignoriert.

Hinweis: Wenn Sie einen istringstream in einer Schleife verwenden, müssen Sie clear () aufrufen, bevor Sie ihm eine neue Zeichenfolge zuweisen.

//config.txt
//Input name = image1.png
//Num. of rows = 100
//Num. of cols = 150

std::string ipName;
int nR, nC;

std::ifstream fin("config.txt");
std::string line;
std::istringstream sin;

while (std::getline(fin, line)) {
 sin.str(line.substr(line.find("=")+1));
 if (line.find("Input name") != std::string::npos) {
  std::cout<<"Input name "<<sin.str()<<std::endl;
  sin >> ipName;
 }
 else if (line.find("Num. of rows") != std::string::npos) {
  sin >> nR;
 }
 else if (line.find("Num. of cols") != std::string::npos) {
  sin >> nC;
 }
 sin.clear();
}
1
haripkannan

Ich suchte nach etwas, das wie das Python-Modul ConfigParser funktionierte, und fand Folgendes: https://github.com/jtilly/inih

Dies ist nur eine C++ - Version von inih.

inih (INI hier nicht erfunden) ist ein einfacher INI-Dateiparser, der in .__ geschrieben wird. C. Es sind nur ein paar Seiten Code, und es wurde als .__ konzipiert. klein und einfach, also gut für eingebettete Systeme. Es ist auch mehr oder weniger kompatibel mit dem ConfigParser-Stil der .INI-Dateien von Python, einschließlich mehrzeiliger Syntax nach RFC 822 und Namen: Werteinträgen.

1
AaronS