web-dev-qa-db-ja.com

C ++で簡単な構成ファイルとパーサーを作成する

このような単純な構成ファイルを作成しようとしています

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

プログラムの実行時に、以下にリストされているプログラム変数に構成設定をロードしたいと思います。

string url, file;
bool true_false;

私はいくつかの研究を行っており、 this リンクは役立つように見えました(核子の投稿)これを行う簡単な方法はありますか? ifstreamを使用してファイルをロードできますが、それは自分でできる限りです。ありがとう!

50
llk

一般に、このような典型的な設定ファイルを2段階で解析するのが最も簡単です。最初に行を読み取り、次にそれらを1つずつ解析します。
C++では、std::getline()を使用してストリームから行を読み取ることができます。デフォルトでは、次の'\n'(これは消費しますが、戻りません)、他の区切り文字も渡すことができます。これは、=あなたの例では。

簡単にするために、以下では=notで囲まれた空白です。これらの位置に空白を許可する場合は、戦略的に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);
  }
}

(エラー処理の追加は、読者への課題として残されています。)

49
sbi

他の人が指摘したように、ホイールを再発明するよりも、既存の構成ファイルパーサーライブラリを使用するほうがおそらく作業が少なくなります。

たとえば、 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 website は包括的なドキュメントを提供しますが、「Getting Started Guide」の第2章と第3章だけを読むだけで十分です。

33
Ciaran McHale

素朴なアプローチは次のようになります。

#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

13
Kerrek SB

libconfig は非常に簡単で、さらに良いことに、読みやすくするために擬似JSON表記を使用します。

Ubuntuに簡単にインストールできます:Sudo apt-get install libconfig++8-dev

およびリンク:-lconfig++

12
user1382306

JSON(またはXML)のような単純で人間が読めるものを試してみませんか?

C++用のJSON(またはXML)のオープンソース実装が多数あります。そのうちの1つを使用します。

そして、もっと「バイナリ」なものが必要な場合は、BJSONまたはBSONを試してください:)

4
yosh kemu

私は最近、プロジェクトの構成解析ライブラリを検索しましたが、これらのライブラリが見つかりました。

3
Ivan Samygin

設定をJSONとしてフォーマットし、 jsoncpp のようなライブラリを使用してはどうですか?

例えば.

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

その後、名前付き変数に読み込むか、すべてをstd :: mapなどに保存することもできます。後者は、構成パーサーを変更および再コンパイルすることなくオプションを追加できることを意味します。

3
patmanpato

python module ConfigParser のように機能するものを探していましたが、これを見つけました: https://github.com/jtilly/inih

これはinihのヘッダーのみのC++バージョンです。

inih(INIはここでは発明されていません)は、Cで書かれたシンプルな.INIファイルパーサーです。数ページのコードであり、小さくシンプルに設計されているため、組み込みシステムに適しています。また、RFC 822スタイルの複数行構文やname:valueエントリなど、PythonのConfigParserスタイルの.INIファイルとほぼ互換性があります。

2
ams

以下に、構成ファイル内の '='記号とデータの間の空白の簡単な回避策を示します。 「=」記号の後の場所から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();
}
1
haripkannan