Подтвердить что ты не робот

Создание простого файла конфигурации и анализатора в С++

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

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

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

string url, file;
bool true_false;

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

4b9b3361

Ответ 1

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

Для простоты следует, что = не окружены пробелами. Если вы хотите разрешить пробелы на этих позициях, вам нужно стратегически разместить 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);
  }
}

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

Ответ 2

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

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

# 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 "Руководства по началу работы" должно быть более чем достаточно для вашего необходимо.

Ответ 3

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

#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 в любом месте вашей программы. Если вы хотите использовать кастимость, вы можете сделать сопоставленный тип a boost::variant.

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 6

Я недавно искал библиотеки для разбора текста для моего проекта и нашел эти библиотеки:

  • http://www.hyperrealm.com/libconfig/ - небольшая, но мощная и простая в использовании библиотека, примеры использования находятся в исходном пакете и здесь
  • http://pocoproject.org/docs/Poco.Util.PropertyFileConfiguration.html - часть pocoproject. Вы можете использовать абстракцию в своем коде и за ней использовать различные поддерживаемые реализации (ini файлы, Xml-конфигурации и даже переменные среды).

Ответ 7

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

например.

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

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

Ответ 8

Вот простая работа вокруг пробела между знаком "=" и данными в файле конфигурации. Присвоите значение 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();
}

Ответ 9

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

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

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