如何在Boost::program_options配置文件中为非字符串的自定义选项值类型处理空格
How to handle spaces in Boost::program_options config files for custom option value types that are not strings?
此问题涉及Boost::program_options
配置文件中值的解析。
我有一个简单的自定义数据结构:
struct Vector {
double x, y, z;
};
我有一个格式为"(x, y, z)
"的istream反序列化器,我从另一个SO帖子那里借来
// https://codereview.stackexchange.com/a/93811/186081
struct Expect {
char expected;
Expect(char expected) : expected(expected) {}
friend std::istream& operator>>(std::istream& is, Expect const& e) {
char actual;
if ((is >> actual) && (actual != e.expected)) {
is.setstate(std::ios::failbit);
}
return is;
}
};
template<typename CharT>
std::basic_istream<CharT> &
operator>>(std::basic_istream<CharT> &in, Vector &v) {
in >> Expect('(') >> v.x
>> Expect(',') >> v.y
>> Expect(',') >> v.z
>> Expect(')');
return in;
}
我正在使用Vector
的实例作为Boost::program_options
:的值存储
Vector vector {0.0, 0.0, 0.0};
po::options_description opts("Usage");
opts.add_options()
("vector", po::value(&vector), "The vector");
po::variables_map vm;
po::store(po::parse_config_file("config.cfg", opts, true), vm);
po::notify(vm);
问题是,如果向量值表示包含空格,则配置文件格式不起作用。例如,此配置文件正确解析:
vector = (0.0,1.1,2.2)
然而,这与空格无关:
vector = (0.0, 1.1, 2.2)
相反,program_options
抛出:
the argument ('(0.0, 1.1, 2.2)') for option 'vector' is invalid
然而,对于声明为std::string
的选项,空格似乎是可以的:
some_string = this is a string
我发现一些帖子提到使用引号,但这似乎不起作用(同样的错误(:
vector = "(0.0, 1.1, 2.2)"
其他一些帖子建议使用自定义解析器,但我不确定该如何实现,而且仅仅处理几个空间似乎需要做很多工作。
我认为这种行为来自于命令行选项的解析方式,尽管这是配置文件解析。在这种情况下,像--vector (0.0, 1.1, 2.2)
这样的命令行没有多大意义(暂时忽略外壳保留字符(
和)
的使用(
有什么好办法处理这个问题吗?
编辑:经过深思熟虑,我认为你可以尝试修改分隔符,如https://en.cppreference.com/w/cpp/locale/ctype
CCD_ 10使用CCD_ 11,后者要求在操作员>>之后消耗全部内容。当有空间时,默认情况下,内容永远不会被一个>>占用,因此会出现错误。
因此,您可以执行以下操作:
struct Optional {
char optional;
Optional(char optional):optional(optional){}
friend std::istream& operator>>(std::istream& is, Optional const& o) {
char next;
do{
next = is.peek();
}while(next == o.optional && is.get());
return is;
}
};
struct vector_ctype : std::ctype<wchar_t> {
bool do_is(mask m, char_type c) const {
if ((m & space) && c == L' ') {
return false; // space will NOT be classified as whitespace
}
return ctype::do_is(m, c); // leave the rest to the parent class
}
};
template<typename CharT>
std::basic_istream<CharT> &
operator>>(std::basic_istream<CharT> &in, Vector &v) {
std::locale default_locale = in.getloc();
in.imbue(std::locale(default_locale, new vector_ctype()));
in >> Expect('(') >> Optional(' ') >> v.x >> Optional(' ')
>> Expect(',') >> Optional(' ') >> v.y >> Optional(' ')
>> Expect(',') >> Optional(' ') >> v.z >> Optional(' ')
>> Expect(')');
in.imbue(default_locale);
return in;
}
int main()
{
Vector v = boost::lexical_cast<Vector>("(1, 2, 3)");
std::cout << v.x <<"," <<v.y <<"," << v.z <<std::endl;
}
输出:
1,2,3
这应该会在程序选项中为您提供正确的输出
您可以编写一个例程来过滤输入文件中不需要的字符,然后将其传递给boost::program_options
进行解析,而不是编写自定义解析器。
在C++中使用SO答案从std::string中删除空格,下面是一个基于代码的工作示例:
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
struct Vector {
double x, y, z;
};
// https://codereview.stackexchange.com/a/93811/186081
struct Expect {
char expected;
Expect(char expected) : expected(expected) {}
friend std::istream& operator>>(std::istream& is, Expect const& e) {
char actual;
if ((is >> actual) && (actual != e.expected)) {
is.setstate(std::ios::failbit);
}
return is;
}
};
template<typename CharT>
std::basic_istream<CharT> &
operator>>(std::basic_istream<CharT> &in, Vector &v) {
in >> Expect('(') >> v.x
>> Expect(',') >> v.y
>> Expect(',') >> v.z
>> Expect(')');
return in;
}
std::stringstream filter(const std::string& filename)
{
std::ifstream inputfile(filename);
std::stringstream s;
std::string line;
while (std::getline(inputfile, line))
{
auto end_pos = std::remove(line.begin(), line.end(), ' ');
line.erase(end_pos, line.end());
s << line << 'n';
}
s.seekg(0, std::ios_base::beg);
return s;
}
int main()
{
namespace po = boost::program_options;
po::options_description opts("Usage");
opts.add_options()
("vector", po::value<Vector>(), "The vector");
auto input = filter("config.cfg");
po::variables_map vm;
po::store(po::parse_config_file(input, opts, true), vm);
po::notify(vm);
auto read_vec = vm["vector"].as<Vector>();
std::cout << "vector is : {" << read_vec.x << ", " << read_vec.y << ", " << read_vec.z << "}" << std::endl;
return 0;
}
要测试程序,您必须创建一个文件config.cfg
,其中包含,例如:
vector = (1, 2, 3)
(额外的间距是故意的,用来测试程序(。
这样,程序的输出就是
vector is : {1, 2, 3}
- 如何使用运算符>>在自定义字符串中输入多个单词?
- 字符串流中的自定义字符串输入
- C++ 使用链表的自定义字符串类中的重载 + 运算符
- 如何在 C++ 中安全地为 char *array 重新分配内存(它适用于自定义字符串类)
- Xcode 4.5.2 libc++ std::bad_cast 实现自定义(字符串)流时
- 自定义字符串类实现建议
- 自定义字符串类中的常量字符*差异
- 如何用C++递归地从二进制文件中读取自定义字符串
- 自定义字符串排序
- 带有自定义字符串比较器的字符串的priority_queue的C++向量
- 自定义字符串文字
- 为什么标准字符串函数比我的自定义字符串函数快
- 自定义字符串实现中 c_str() 函数中的恒常性问题
- 自定义字符串实现,运算符 + 过载内存分配问题
- 自定义字符串类和析构函数
- 在C++中的自定义字符串类中实现插入
- 自定义字符串类中的Segfault
- 打印自定义字符串的数据时出现奇怪的输出(c++新手)
- 将facet应用于所有流输出,使用自定义字符串操作符
- 使用stringstreams的c++自定义字符串格式