Boost parse_config_file,空键值 [英] Boost parse_config_file, empty key value

查看:78
本文介绍了Boost parse_config_file,空键值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Boost program_options以标准方式解析配置文件,如下所示:
显示在program_options的multi_sources.cpp示例文件中.

I am using Boost program_options to parse a config file in the standard way as
shown in the multiple_sources.cpp example file of program_options.

    ifstream ini_file("config.ini");  
    po::store(po::parse_config_file(ini_file, desc, true), vm);  
    po::notify(vm);

但是config.ini文件具有空的key = value对,例如:

The config.ini file however has empty key=value pairs such as:

key1=value1  
key2=value2  
key3=  
key4=  
key5=value5  

尝试读取此文件会导致Boost错误:

Trying to read this file results in a Boost error:

boost::program_options::invalid_option_value

boost :: program_options中是否有任何机制可以读取带有空整的配置文件?

Is there any mechanism in boost::program_options to read such config files with empty entires?

任何帮助将不胜感激.

由于该问题尚未解决,因此我正在编辑此问题.我只是引用Boost(1.53)中的示例.

I am editing this Question since the the problem has not yet been solved. I will just cite the example from Boost (1.53).

这是完整的multiple_sources.cpp文件:

This is the full multiple_sources.cpp file:

#include <boost/program_options.hpp>
namespace po = boost::program_options;


#include <iostream>
#include <fstream>
#include <iterator>
using namespace std;

// A helper function to simplify the main part.
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v)
{
    copy(v.begin(), v.end(), ostream_iterator<T>(os, " ")); 
    return os;
}


int main(int ac, char* av[])
{
    try {
        int opt;
        string config_file;

        // Declare a group of options that will be 
        // allowed only on command line
        po::options_description generic("Generic options");
        generic.add_options()
            ("version,v", "print version string")
            ("help", "produce help message")
            //("optimization","optimization level")      
            ("config,c", po::value<string>(&config_file)->default_value("multiple_sources.cfg"),
                  "name of a file of a configuration.")
            ;

        // Declare a group of options that will be 
        // allowed both on command line and in
        // config file
        po::options_description config("Configuration");
        config.add_options()
            ("optimization", po::value<int>(&opt)->default_value(10), 
                  "optimization level")
            ("include-path,I", po::value< vector<string> >()->composing(), 
                 "include path")
            ;

        // Hidden options, will be allowed both on command line and
        // in config file, but will not be shown to the user.
        po::options_description hidden("Hidden options");
        hidden.add_options()
            ("input-file", po::value< vector<string> >(), "input file")
            ;

        po::options_description cmdline_options;
        cmdline_options.add(generic).add(config).add(hidden);

        po::options_description config_file_options;
        config_file_options.add(config).add(hidden);

        po::options_description visible("Allowed options");
        visible.add(generic).add(config);

        po::positional_options_description p;
        p.add("input-file", -1);

        po::variables_map vm;
        store(po::command_line_parser(ac, av).
              options(cmdline_options).positional(p).run(), vm);
        notify(vm);

        ifstream ifs(config_file.c_str());
        if (!ifs)
        {
            cout << "can not open config file: " << config_file << "\n";
            return 0;
        }
        else
        {
            store(parse_config_file(ifs, config_file_options), vm);
            notify(vm);
        }

        if (vm.count("help")) {
            cout << visible << "\n";
            return 0;
        }

        if (vm.count("version")) {
            cout << "Multiple sources example, version 1.0\n";
            return 0;
        }

        if (vm.count("include-path"))
        {
            cout << "Include paths are: " 
                 << vm["include-path"].as< vector<string> >() << "\n";
        }

        if (vm.count("input-file"))
        {
            cout << "Input files are: " 
                 << vm["input-file"].as< vector<string> >() << "\n";
        }

        cout << "Optimization level is " << opt << "\n";                
    }
    catch(exception& e)
    {
        cout << e.what() << "\n";
        return 1;
    }    
    return 0;
}

对应的配置文件(multiple_sources.cfg)为:

And the corresponding configuration file (multiple_sources.cfg) is:

#
# Comment out this line to use hard-coded default value of 10
# 
optimization = 1
include-path = /opt

如果此文件现在已修改为:

If this file is now modified to:

#
# Comment out this line to use hard-coded default value of 10
# 
optimization = 
include-path = /opt

将引发以下错误消息:

the argument for option 'optimization' is invalid

所提出的带有验证重载等的解决方案似乎不必要地复杂,特别是因为可能必须为每种数据类型编写一个验证函数,并包含识别"\ n"其他空白的可能性.

The proposed solutions with validation overloading etc. seem unnecessarily complicated, especially since one might have to write a validation function for each data type, incorporating the possibility of recognizing '\n' other white-space.

有什么建议吗?谢谢大家抽出宝贵的时间.

Any suggestions? Thank you everyone for taking the time.

按照Aditya的建议,我替换了以下行:

Following Aditya's suggestion I have replaced the following line :

            ("optimization", po::value<int>(&opt)->default_value(10), 
                  "optimization level")

具有以下内容:

            ("optimization", po::value<int>(&opt)->zero_tokens(), 
                  "optimization level")

和:

            ("optimization", po::value<int>(&opt)->implicit_value(10), 
                  "optimization level")

,它们都不读取空白选项.Boost示例程序"parser_test.cpp"通过将键-值对读入向量,从而绕过了zero_tokens()或implicit_value()的使用:

and neither of them read blank options. Boost example program "parser_test.cpp" bypasses the use of zero_tokens(), or implicit_value() by reading the key-value pairs into a vector as follows:

void test_config_file(const char* config_file)
{
    options_description desc;
    desc.add_options()
        ("gv1", new untyped_value)
        ("gv2", new untyped_value)
        ("empty_value", new untyped_value)
        ("plug*", new untyped_value)
        ("m1.v1", new untyped_value)
        ("m1.v2", new untyped_value)
        ("b", bool_switch())
    ;

    const char content1[] =
    " gv1 = 0#asd\n"
    "empty_value = \n"
    "plug3 = 7\n"
    "b = true\n"
    "[m1]\n"
    "v1 = 1\n"
    "\n"
    "v2 = 2\n"    
    ;

    vector<option> a2 = parse_config_file<char>(config_file, desc).options;
    BOOST_REQUIRE(a2.size() == 6);
    check_value(a2[0], "gv1", "0");
    check_value(a2[1], "empty_value", "");
    check_value(a2[2], "plug3", "7");
    check_value(a2[3], "b", "true");
    check_value(a2[4], "m1.v1", "1");
    check_value(a2[5], "m1.v2", "2");
}

推荐答案

我几次无意中发现了这个问题,最后通过将初始INI文件中的无效行删除然后再传递给 program_options ::来解决了这个问题:parse_config_file .可以通过在文件系统上创建一个临时的修改后的INI文件或将整个原始INI文件读取到内存中并在其中进行修改,然后创建 istringstream 并传递给 parse_config_file .

I stumbled on this problem several times and finally solved it by just removing invalid lines from the initial INI-file before passing it to the program_options::parse_config_file. It can be done by creating a temporary modified INI-file on filesystem or reading the whole original INI-file to memory and modifying it there, and then create istringstream to pass to parse_config_file.

我个人创建了一个 boost :: iostreams 过滤流类,用于动态过滤 ifstream .这不会创建任何新的依赖项,因为我已经使用了Boost,并且在我的情况下,只能在标头中使用 boost :: iostreams .

I, personally, created a boost::iostreams filtering stream class for this purpose to filter ifstream on the fly. This does not create any new dependency, since I already use Boost, and boost::iostreams can be used header-only in my case.

这是它的用法:

#include <fstream>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/program_options.hpp>
#include "inifilefilter.h" // IniFileInputFilter
...
std::ifstream file("config.ini");
if (file)
{
   // filtering out empty values, i.e. name=
   boost::iostreams::filtering_istream filteredIniFile; 
   filteredIniFile.push(IniFileInputFilter());
   filteredIniFile.push(file);
   po::parsed_options parsedFileOpts =
         po::parse_config_file(filteredIniFile, configFileOptions, true);
   ...
}

在这里我不会显示 inifilefilter.h 中的代码,因为它在所有检查和极端情况下都有些冗长,但同时,它的想法非常简单. IniFileInputFilter read()方法缓冲每条输入行,然后在行尾仅在有效时才进一步传递该行.有效"表示它是 [section] 行,或者在第一个'='和行尾之间有一些非空格字符.

I won't show the code from the inifilefilter.h here, because it somewhat lengthy with all the checks and corner cases, but at the same time it's pretty simple in it's idea. The IniFileInputFilter's read() method buffers each input line, and then at the end-of-line passes the line further only if it's valid. And 'valid' means that it is either a [section] line, or have some non-space characters between the first '=' and the end-of-line.

这篇关于Boost parse_config_file,空键值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆