Crear un fichero de configuración y un analizador sintáctico sencillos en C++


Estoy tratando de crear un archivo de configuración simple que se vea así

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

Cuando se ejecuta el programa, me gustaría que cargara los ajustes de configuración en las variables de programas que se enumeran a continuación.

string url, file;
bool true_false;

He hecho algunas investigaciones y este enlace parecía ayudar (el post de nucleon), pero parece que no puedo hacerlo funcionar y es demasiado complicado de entender por mi parte. ¿Hay una forma sencilla de hacer esto? Puedo cargar el archivo usando ifstream pero eso es todo lo que puedo voy por mi cuenta. ¡Gracias!

Author: llk, 2011-08-01

8 answers

En general, es más fácil analizar estos archivos de configuración típicos en dos etapas: primero leer las líneas y luego analizarlas una por una.
En C++, las líneas se pueden leer desde una secuencia usando std::getline(). Si bien de forma predeterminada se leerá hasta el siguiente '\n' (que consumirá, pero no devolverá), también puede pasarle otro delimitador, lo que lo convierte en un buen candidato para leer hasta algún carácter, como = en su ejemplo.

Para simplificar, lo siguiente supone que los = son no rodeado de espacios en blanco. Si desea permitir espacios en blanco en estas posiciones, tendrá que colocar estratégicamente is >> std::ws antes de leer el valor y eliminar los espacios en blanco finales de las claves. Sin embargo, IMO la poca flexibilidad añadida en la sintaxis no vale la pena para un lector de archivos de configuración.

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);
  }
}

(Agregar el manejo de errores se deja como un ejercicio para el lector.)

 40
Author: sbi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-07 19:58:17

Como otros han señalado, probablemente será menos trabajo hacer uso de una biblioteca de analizador de archivos de configuración existente en lugar de reinventar la rueda.

Por ejemplo, si decide usar la biblioteca Config4Cpp (que mantengo), entonces la sintaxis del archivo de configuración será ligeramente diferente (ponga comillas dobles alrededor de los valores y termine las instrucciones de asignación con un punto y coma) como se muestra en el siguiente ejemplo:

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

El siguiente programa analiza lo anterior archivo de configuración, copia los valores deseados en variables y las imprime:

#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;
}

El sitio web de Config4Cpp proporciona documentación completa, pero leer solo los capítulos 2 y 3 de la "Guía de Introducción" debería ser más que suficiente para sus necesidades.

 31
Author: Ciaran McHale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-01 14:53:46

Un enfoque ingenuo podría verse así:

#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;
        }
    }
}

Ahora puede acceder a cada valor de opción desde el mapa global options en cualquier lugar de su programa. Si desea castability, puede hacer que el tipo asignado sea a boost::variant.

 12
Author: Kerrek SB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-14 15:02:30

Libconfig es muy fácil, y lo que es mejor, utiliza una notación pseudo json para una mejor legibilidad.

Fácil de instalar en Ubuntu: sudo apt-get install libconfig++8-dev

Y enlace: -lconfig++

 10
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-08 21:12:14

¿Por qué no probar algo simple y legible, como JSON (o XML) ?

Hay muchas implementaciones de código abierto pre-hechas de JSON (o XML) para C++-yo usaría una de ellas.

Y si quieres algo más "binario" - prueba BJSON o BSON:)

 3
Author: yosh kemu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-31 22:36:16

¿Qué tal formatear su configuración como JSON, y usar una biblioteca como jsoncpp?

Por ejemplo

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

Luego puede leerlo en variables con nombre, o incluso almacenarlo todo en un mapa std::, etc. Esto último significa que puede agregar opciones sin tener que cambiar y recompilar su analizador de configuración.

 3
Author: patmanpato,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-25 00:04:42

He buscado bibliotecas de análisis de configuración para mi proyecto recientemente y encontré estas bibliotecas:

 2
Author: Ivan Samygin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-30 04:52:07

Aquí hay un simple trabajo alrededor del espacio en blanco entre el signo '=' y los datos, en el archivo de configuración. Asignar a lastreamingstream desde la ubicación después del signo ' = ' y al leer desde él, cualquier espacio en blanco inicial se ignora.

Nota: mientras usa unstreamingstream en un bucle, asegúrese de llamar a clear() antes de asignarle una nueva cadena.

//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
Author: haripkannan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-24 08:23:47