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
$