使用Boost::Spirit解析具有未知键的"键=值"列表
Parsing list of 'key = value' with unknown keys with Boost::Spirit
我有一个像下面这样的字符串:
[GENERAL]
FMax Antenna = 3000
FMin Antenna = 2000
Invalid key = Invalid value
EMin Antenna = -50
EMax Antenna = 80
我想解析它,以便将FMin Antenna
、FMax Antenna
、EMin Antenna
、EMax Antenna
的值保存在结构中。我创建了一个精神解析器,但它部分工作。 由于该文件可以有很多key = value
行,因此我只需要解析我需要的内容(我必须读取的键值),而忽略其他对。
key
和value
都可以是带有空格和制表符的字母数字字符串。
我已经在解析器中定义了我要读取的键,但是当我遇到未知键时,我无法读取它后面的键(在示例示例中,我无法读取EMin Antenna
和EMax Antenna
因为在未知键之后定义)。
我已经尝试了下面的代码: 如果我解析file1
,它只包含我想读取的键,它可以工作,但是如果我在文件中间添加未知的key = value
对,就像在file2
中一样,它会停止读取所有后续行。
如何修复它并在未知键值对后继续解析文件?
#include <boost/optional/optional_io.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
const std::string file1 = R"xx(
[GENERAL]
FMax Antenna = 3000
FMin Antenna = 2000
EMin Antenna = -50
EMax Antenna = 80
)xx";
const std::string file2 = R"xx(
[GENERAL]
FMax Antenna = 3000
FMin Antenna = 2000
EMin Antenna = -50
pappa pio = po po
EMax Antenna = 80
Ciao = 55
)xx";
struct Data {
double minFrequency = 0.0;
double maxFrequency = 0.0;
double minElevation = 0.0;
double maxElevation = 0.0;
};
BOOST_FUSION_ADAPT_STRUCT(
Data,
(double, minFrequency)
(double, maxFrequency)
(double, minElevation)
(double, maxElevation)
)
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, Data(), Skipper> {
grammar() : grammar::base_type(start) {
auto minFrequency = bind(&Data::minFrequency, qi::_val);
auto maxFrequency = bind(&Data::maxFrequency, qi::_val);
auto minElevation = bind(&Data::minElevation, qi::_val);
auto maxElevation = bind(&Data::maxElevation, qi::_val);
start = qi::no_case["[GENERAL]"] >> *(
("FMin Antenna" >> qi::lit('=') >> qi::int_)[minFrequency = qi::_1] |
("FMax Antenna" >> qi::lit('=') >> qi::int_)[maxFrequency = qi::_1] |
("EMin Antenna" >> qi::lit('=') >> qi::int_)[minElevation = qi::_1] |
("EMax Antenna" >> qi::lit('=') >> qi::int_)[maxElevation = qi::_1] |
(+(qi::alnum | qi::blank) >> qi::lit('=') >> +(qi::alnum | qi::blank)) // Issue here?
);
}
private:
qi::rule<It, Data(), Skipper> start;
};
int main() {
using It = std::string::const_iterator;
Data parsed1, parsed2;
bool ok = qi::phrase_parse(file1.begin(), file1.end(), grammar<It>(), qi::space, parsed1);
std::cout << "--- File 1 ---" << std::endl;
std::cout << "parsed = " << std::boolalpha << ok << std::endl;
std::cout << "min freq = " << parsed1.minFrequency << std::endl;
std::cout << "max freq = " << parsed1.maxFrequency << std::endl;
std::cout << "min elev = " << parsed1.minElevation << std::endl;
std::cout << "max elev = " << parsed1.maxElevation << std::endl;
std::cout << "--- File 2 ---" << std::endl;
ok = qi::phrase_parse(file2.begin(), file2.end(), grammar<It>(), qi::space, parsed2);
std::cout << "parsed = " << std::boolalpha << ok << std::endl;
std::cout << "min freq = " << parsed2.minFrequency << std::endl;
std::cout << "max freq = " << parsed2.maxFrequency << std::endl;
std::cout << "min elev = " << parsed2.minElevation << std::endl;
std::cout << "max elev = " << parsed2.maxElevation << std::endl;
return 0;
}
输出:
--- File 1 ---
parsed = true
min freq = 2000
max freq = 3000
min elev = -50
max elev = 80
--- File 2 ---
parsed = true
min freq = 2000
max freq = 3000
min elev = -50
max elev = 0 <-- This should be 80 like in the first parsing
你对船长感到困惑。
- 换行符在你的语法中很重要,这就是为什么你需要一个不吃它们的船长
- 在你的规则中,你匹配
+(alnum|blank)
这永远不会起作用,因为船长无论如何都会吃掉所有匹配blank
的东西
(有关背景,请参阅提升精神船长问题)
其他注意事项:
- 除非你想要自动魔术属性传播,否则你不需要融合适应。你现在没有使用它。
解决它
我会把事情说得非常明确:
known =
("FMin Antenna" >> lit('=') >> int_)[minFrequency = _1] |
("FMax Antenna" >> lit('=') >> int_)[maxFrequency = _1] |
("EMin Antenna" >> lit('=') >> int_)[minElevation = _1] |
("EMax Antenna" >> lit('=') >> int_)[maxElevation = _1]
;
unknown = +alnum >> '=' >> +alnum;
setting = (known(_r1) | unknown) >> +eol;
start =
no_case["[GENERAL]"] >> eol
>> *setting(_val);
在规则中拆分有点棘手,因为*setting
会尝试合成容器属性,从而无法传播到实际的Data
属性。
我通过在继承属性中通过引用传递属性来解决它,这会禁用自动属性传播。
或者,您可以添加任何类型的语义操作来禁止自动属性传播
演示
住在科里鲁
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
struct Data {
double minFrequency = 0.0;
double maxFrequency = 0.0;
double minElevation = 0.0;
double maxElevation = 0.0;
};
template <typename It, typename Skipper = qi::blank_type>
struct grammar : qi::grammar<It, Data(), Skipper> {
grammar() : grammar::base_type(start) {
using namespace qi;
auto minFrequency = bind(&Data::minFrequency, _r1);
auto maxFrequency = bind(&Data::maxFrequency, _r1);
auto minElevation = bind(&Data::minElevation, _r1);
auto maxElevation = bind(&Data::maxElevation, _r1);
known =
("FMin Antenna" >> lit('=') >> int_)[minFrequency = _1] |
("FMax Antenna" >> lit('=') >> int_)[maxFrequency = _1] |
("EMin Antenna" >> lit('=') >> int_)[minElevation = _1] |
("EMax Antenna" >> lit('=') >> int_)[maxElevation = _1]
;
unknown = +alnum >> '=' >> +alnum;
setting = (known(_r1) | unknown) >> +eol;
start =
no_case["[GENERAL]"] >> eol
>> *setting(_val);
}
private:
qi::rule<It, Data(), Skipper> start;
qi::rule<It, void(Data&), Skipper> setting, known;
qi::rule<It, Skipper> unknown;
};
int main() {
using It = std::string::const_iterator;
grammar<It> const g;
for (std::string const file : {
"[GENERAL]nFMax Antenna = 3000nFMin Antenna = 2000nEMin Antenna = -50nEMax Antenna = 80n",
"[GENERAL]nFMax Antenna = 3000nFMin Antenna = 2000nEMin Antenna = -50npappa pio = po ponEMax Antenna = 80nCiao = 55n",
})
{
Data parsed;
It f = begin(file), l = end(file);
bool ok = qi::phrase_parse(f, l, g, qi::blank, parsed);
std::cout << "--- File ---" << "n";
std::cout << "parsed = " << std::boolalpha << ok << "n";
if (ok) {
std::cout << "min freq = " << parsed.minFrequency << "n";
std::cout << "max freq = " << parsed.maxFrequency << "n";
std::cout << "min elev = " << parsed.minElevation << "n";
std::cout << "max elev = " << parsed.maxElevation << "n";
}
if (f!=l) {
std::cout << "Remaining unparsed: ";
while (f!=l) {
char c = *f++;
if (isprint(c)) std::cout << c;
else std::cout << "\x" << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(c);
}
}
}
}
指纹
--- File ---
parsed = true
min freq = 2000
max freq = 3000
min elev = -50
max elev = 80
--- File ---
parsed = true
min freq = 2000
max freq = 3000
min elev = -50
max elev = 80
相关文章:
- Pybind11:将元组列表从Python传递到C++
- 从链接列表c++中删除一个项目
- 如何(从固定列表中)选择一个数字序列,该序列将与目标数字相加
- C++如何通过用户输入删除列表元素
- 读取文件的最后一行并输入到链接列表时出错
- 继承期间显示未知行为的子类
- 复制列表初始化的隐式转换的等级是多少
- LNK2038、MSVS2017 MAGMA的原因列表
- 不能在初始值设定项列表中将非常量表达式从类型 'int' 缩小到'unsigned long long'
- 没有为自己的结构调用列表推回方法
- 使用简单类型列表实现的指数编译时间.为什么
- C++未知长度字符串的数组,其行为类似于 Python 字符串列表
- 将参数列表未知的可变参数模板类型定义为 std::map 值类型
- 未知("模板化")类数量的初始值设定项列表
- std :: list(双重链接列表)未知运行时错误
- 构造函数初始化器列表中未知长度的数组
- 使用Boost::Spirit解析具有未知键的"键=值"列表
- 对未知大小数组的引用的列表初始化:它是否应该推断数组大小?
- 写邻接列表图时出现未知错误
- c++ Boost循环通过未知的ptree和列表属性值