Создание простого файла конфигурации и анализатора в C ++ - PullRequest
49 голосов
/ 01 августа 2011

Я пытаюсь создать простой файл конфигурации, который будет выглядеть следующим образом:

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

при запуске программы, я хотел бы загрузить параметры конфигурации в переменные программы, перечисленные ниже.

string url, file;
bool true_false;

Я провел некоторое исследование, и эта ссылка , похоже, помогла (сообщение нуклона), но я не могу заставить ее работать, и она слишком сложна для понимания с моей стороны.Есть ли простой способ сделать это?Я могу загрузить файл, используя ifstream, но это все, что я могу сделать самостоятельно.Спасибо!

Ответы [ 9 ]

49 голосов
/ 01 августа 2011

В общем, такие типичные файлы конфигурации проще всего анализировать в два этапа: сначала прочитать строки, а затем проанализировать их по одному.
В C ++ строки можно читать из потока, используя std::getline().Хотя по умолчанию он будет читать до следующего '\n' (который он будет потреблять, но не возвращать), вы также можете передать ему какой-то другой разделитель, что делает его хорошим кандидатом для чтения до некоторого символа,как = в вашем примере.

Для простоты следующее предполагает, что = не окружены пробелами.Если вы хотите разрешить пробелы в этих позициях, вам нужно стратегически разместить is >> std::ws перед чтением значения и удалить конечные пробелы из ключей.Однако, IMO, небольшая добавленная гибкость в синтаксисе не стоит хлопот для читателя файла конфигурации.

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

(Добавление обработки ошибок оставлено читателю в качестве упражнения.)

33 голосов
/ 01 августа 2011

Как уже отмечали другие, вероятно, будет меньше работы, чтобы использовать существующую библиотеку синтаксического анализатора файла конфигурации, чем заново изобретать колесо.

Например, если вы решите использовать библиотеку Config4Cpp (которую я поддерживаю), то синтаксис вашего файла конфигурации будет немного другим (заключите двойные кавычки вокруг значений и завершите операторы присваивания точкой с запятой) как показано в примере ниже:

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

Следующая программа анализирует указанный выше файл конфигурации, копирует нужные значения в переменные и печатает их:

#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 предоставляет исчерпывающую документацию, но чтение только глав 2 и 3 «Руководства по началу работы» должно быть более чем достаточным для ваших нужд.

13 голосов
/ 01 августа 2011

Наивный подход может выглядеть так:

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

Теперь вы можете получить доступ к каждому значению опции из глобальной карты options в любой точке вашей программы. Если вы хотите получить возможность литья, вы можете сделать отображаемый тип boost::variant.

12 голосов
/ 09 февраля 2014

libconfig очень прост, и, что еще лучше, он использует псевдо-json нотацию для лучшей читаемости.

Простота установки в Ubuntu: sudo apt-get install libconfig++8-dev

и ссылка: -lconfig++

4 голосов
/ 01 августа 2011

Почему бы не попробовать что-нибудь простое и понятное человеку, например, JSON (или XML)?

Существует множество готовых реализаций JSON (или XML) с открытым исходным кодом для C ++ - я бы использовал одну из них.

А если вы хотите что-то более "двоичное" - попробуйте BJSONили BSON:)

3 голосов
/ 25 ноября 2015

Как насчет форматирования вашей конфигурации в формате JSON и использования такой библиотеки, как jsoncpp ?

, например

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

Затем вы можете прочитать его в именованные переменные или даже сохранить все в std :: map и т. Д. Последнее означает, что вы можете добавлять опции без необходимости изменять и перекомпилировать свой анализатор конфигурации.

3 голосов
/ 29 января 2014

Я недавно искал библиотеки синтаксического анализа конфигурации для своего проекта и нашел следующие библиотеки:

2 голосов
/ 18 января 2019

Я искал что-то, что работало как модуль python ConfigParser и нашел это: https://github.com/jtilly/inih

Это версия inih только для заголовков C ++.

inih (INI не изобретен здесь) - это простой синтаксический анализатор .INI-файлов, написанный на C. Это всего лишь пара страниц кода, и он был разработан, чтобы быть маленьким и простым, поэтому он хорош для встроенных систем.Он также более или менее совместим со стилем Python ConfigParser .INI-файлов, включая многострочный синтаксис в стиле RFC 822 и записи name: value.

1 голос
/ 24 апреля 2015

Вот простой способ обойти пробел между знаком «=» и данными в файле конфигурации. Присвойте потоку istringstream из местоположения после знака '=' и при чтении с него все начальные пробелы игнорируются.

Примечание: при использовании istringstream в цикле обязательно вызовите clear (), прежде чем присваивать ему новую строку.

//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();
}
...