How to create and read ".conf" files in C, for linux environment?

8

I'm studying C in linux environment.

I have a problem with how to create a configuration file for my program to know what to do when running.

Question

  • Does anyone know how to store and read variables in .conf files?
  • Is there a technique for this?
  • What is the default?
asked by anonymous 05.10.2016 / 09:53

3 answers

2

Use Lib confuse, everything is ready ... it's just magical ... here is the original link of the subject: link

You can download directly from their link:

http://savannah.nongnu.org/download/confuse/

install it easy:

$ ./configure
$ make
$ su -c 'make install'

If it's not in dir / usr / include, copy:

$ cd src/
$ su -c 'cp confuse.h /usr/include'

Look at an example use in the article itself:

int main(void)
{
   int porta;
   char *servidor = NULL;
   cfg_t *cfg;
   cfg_opt_t opts[] = {
             CFG_SIMPLE_STR ("server", &servidor),
             CFG_SIMPLE_INT ("port", &porta),
             CFG_END()
             };
   cfg = cfg_init (opts, 0);
   cfg_parse (cfg, "exemplo.conf");

   printf ("\nServidor: %s \n", servidor);
   printf ("Porta: %d \n", porta);
   cfg_free(cfg);       /* Libera a memória alocada por cfg_init */
   return 0;
} 

example of .config file = example.conf

 # Arquivo de configuração para teste.

server = "www.kernel.org"
port = 80

# Fim do arquivo de configuração. 
    
14.10.2016 / 12:53
13

The extension of a configuration file is not only limited to .conf , it can also be .cnf .conf , .cfg , or .ini for example. There is no default how a configuration file can be called.

You can do the reading manually, but it is a rather labor-intensive task, you can use fopen to open the file for reading, fgets to read row by row, and sscanf to parse the line and get the values.

This approach may be somewhat limited, it may work in certain situations where you know the file structure, but it may stop working if you have one more space or line. See here an implementation .

In Windows you can use the GetPrivateProfileString APIs to return a value, GetPrivateProfileInt to return an integer and GetPrivateProfileSection to return the keys and values of a section.

To Qt you can use the functions of the QSettings .

There is no native option for reading a configuration file, but there are alternatives like:

  • Libconfig : C / C ++. Available for GNU / Linux, Mac OS X, Solaris, FreeBSD, Android, and Windows. Documentation .
  • LibIni : C, C ++, Visual Basic, Java, TCL, Perl, Python. Available for Windows, DOS, GNU / Linux.
  • libConfuse : C. Like the others above, it is also available for Windows and Unix-based systems. Tutorial .
  • inih : C / C ++. It was designed to be lightweight and simple, the developer of this project seems to have prioritized this to be compatible with embedded systems.
  • Among others ...

To create an example I will use inih , the code is an adaptation of this documentation example .

  • Assuming you have a foo.cfg configuration file as below:

    ; Exemplo de arquivo de configuração
    
    [usuario]
    nome = João Silva ; Os espaços entre "=" são removidos
    peso = 90.5       ; Valor de ponto flutuante
    idade = 100       ; Valor inteiro
    ativo = true      ; Valor booleano
    
    [local]
    cidade = Rio Branco ; Os comentários serão ignorados (como esse)
    estado = Acre
    
  • To save the information that will be read from the file, we will use the structure below:

    typedef struct {
        const char* nome;
        float peso;
        int idade;
        bool ativo;
    
        const char* cidade;
        const char* estado;
    } configuracao;
    
  • As mentioned in documentation , use a handler to identify the section and key and save the value in the structure. The handler / callback is called every time a chave=valor is parsed. In this example, we can do this:

    static int handler(void* usuario, const char* secao, const char* nome, const char* valor)
    {
        configuracao* cfg = (configuracao*)usuario;
        #define MATCH(s, n) strcmp(secao, s) == 0 && strcmp(nome, n) == 0
    
        /* Identifica a seção e chave e guarda seu valor na estrutura */
    
        if (MATCH("usuario", "nome")) {
            cfg->nome = strdup(valor);
        } else if (MATCH("usuario", "peso")) {
            cfg->peso = atof(valor);
        } else if (MATCH("usuario", "idade")) {
            cfg->idade = atoi(valor);
        } else if (MATCH("usuario", "ativo")) {
            cfg->ativo = (bool)valor;
    
        } else if (MATCH("local", "cidade")) {
            cfg->cidade = strdup(valor);
        } else if (MATCH("local", "estado")) {
            cfg->estado = strdup(valor);
    
        } else {
            return 0;  /* Seção desconhecida ou chave inválida */
        }
        return 1;
    }
    
  • Finally, in the main function of the program, main() , you use the ini_parse function to load the configuration file:

    int main(int argc, char* argv[])
    {
        configuracao cfg;
    
        if (ini_parse("foo.cfg", handler, &cfg) < 0) {
            printf("Não foi possível carregar 'foo.cfg'\n");
            return 1;
        }
        printf("Configurações carregadas do arquivo 'foo.cfg'\n");
    
        printf("Nome: %s\nPeso: %f\nIdade: %d\nAtivo: %d\n", cfg.nome, cfg.peso, 
                                                             cfg.idade, cfg.ativo);
        printf("Cidade: %s, Estado: %s\n", cfg.cidade, cfg.estado);
    
        return 0;
    }
    
  • The complete code looks like this:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdbool.h>
    #include ".ini.h" /* Assumindo que o header esteja no mesmo diretório */
    
    typedef struct {
        const char* nome;
        float peso;
        int idade;
        bool ativo;
    
        const char* cidade;
        const char* estado;
    } configuracao;
    
    static int handler(void* usuario, const char* secao, const char* nome, const char* valor) {
        configuracao* cfg = (configuracao*)usuario;
        #define MATCH(s, n) strcmp(secao, s) == 0 && strcmp(nome, n) == 0
    
        if (MATCH("usuario", "nome")) {
            cfg->nome = strdup(valor);
        } else if (MATCH("usuario", "peso")) {
            cfg->peso = atof(valor);
        } else if (MATCH("usuario", "idade")) {
            cfg->idade = atoi(valor);
        } else if (MATCH("usuario", "ativo")) {
            cfg->ativo = (bool)valor;
    
        } else if (MATCH("local", "cidade")) {
            cfg->cidade = strdup(valor);
        } else if (MATCH("local", "estado")) {
            cfg->estado = strdup(valor);
        } else {
            return 0;  /* Seção desconhecida ou chave inválida */
        }
        return 1;
    }
    
    int main(int argc, char* argv[])
    {
        configuracao cfg;
    
        if (ini_parse("foo.cfg", handler, &cfg) < 0) {
            printf("Não foi possível carregar 'foo.cfg'\n");
            return 1;
        }
        printf("Configurações carregadas do arquivo 'foo.cfg'\n");
    
        printf("Nome: %s\nPeso: %f\nIdade: %d\nAtivo: %d\n", cfg.nome, cfg.peso, 
                                                             cfg.idade, cfg.ativo);
        printf("Cidade: %s, Estado: %s\n", cfg.cidade, cfg.estado);
    
        return 0;
    }
    

    Demo:

    $ gcc -o bar main.c ini.c
    $ ./bar
    Configurações carregadas do arquivo 'foo.cfg'
    Nome: João Silva
    Peso: 90.500000
    Idade: 100
    Ativo: 1
    Cidade: Rio Branco, Estado: Acre
    $
    
        
    07.10.2016 / 19:47
    3

    There are several ways to create configuration files. The configuration files in /etc have their own structure for the user to read.

    If you just want to store the values of your variables in the files so that only the system reads, you can use fwrite and fread .

    This method is only for saving system information, if you try to edit the file in hand, it may give some error and the system may load the information in the wrong way.

    #include <stdio.h>
    #include <string.h>
    
    struct _config{
        char string[16];
        int number;
        float o_number;
        char name[32];
    };
    
    void write_config(const char *fd_conf, struct _config *cfg){
        FILE *f = fopen(fd_conf, "w");
        fwrite(cfg, sizeof(struct _config), 1, f);
        fclose(f);
    }
    
    void read_config(const char *fd_conf, struct _config *cfg){
        FILE *f = fopen(fd_conf, "r");
        fread(cfg, sizeof(struct _config), 1, f);
        fclose(f);
    }
    
    int main(int argc, char **argv){
        struct _config conf = {0};
        conf.number = 10;
        conf.o_number = 3.7f;
        strcpy(conf.string, "texto");
        strcpy(conf.name, "I am a user!");
    
        write_config("file.cfg", &conf);
    
        struct _config conf2 = {0};
        read_config("file.cfg", &conf2);
    
        printf("%d|%f|%s|%s\n",conf2.number, conf2.o_number, conf.string, conf2.name);
    
        return 0;
    }
    
        
    10.10.2016 / 08:17