我有以下文字:
[70000000:45]
4, 5, 6, 7
[60000000:60]
1, 2, 3, 4
[80000:90]
4, 5, 6, 7, 8, 9
带方括号的行包含[freq:angle]
形式的频率和角度,而后续行是与这些参数相关的数字向量。我可以有不同的频率和角度集,并为每个频率和角度定义一个向量。
我有以下结构:
struct Data {
std::vector<int> frequencies;
std::vector<int> elevations;
std::vector<std::vector<double>> gains;
};
我需要在此结构中存储文件数据:在frequencies
向量中,我将按顺序从上到下包含所有频率;在elevations
向量中我会对高程数据有相同的看法,而在gains
向量中我会有各自的增益向量。
范围是,如果我是索引,此索引处的向量元素将包含与文件中相关的频率,高程和增益数据。
例如,解析后,index = 1
我将
data.frequencies[1] = 60000000
data.elevations[1] = 60
data.gains[1] = {1, 2, 3, 4}
我需要解析文件以填充结构。我能够解析frequencies
和elevations
,并将它们存储在向量中,但我无法存储增益数据。我需要从这些数据中创建一个向量矢量。
您可以在下面找到代码;解析后parsed
填充了频率和高程,我需要获得收益。我能做些什么才能解析它们?
#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;
namespace px = boost::phoenix;
const std::string file1 = R"xx(
[70000000:45]
4, 5, 6, 7
[60000000:60]
1, 2, 3, 4
[80000:90]
4, 5, 6, 7, 8, 9
)xx";
struct Data {
std::vector<int> frequencies;
std::vector<int> elevations;
std::vector<std::vector<double>> gains;
};
BOOST_FUSION_ADAPT_STRUCT(
Data,
(std::vector<int>, frequencies)
(std::vector<int>, elevations)
(std::vector<std::vector<double>>, gains)
)
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, Data(), Skipper> {
grammar() : grammar::base_type(start) {
auto frequencyParser = qi::int_[px::push_back(px::at_c<0>(qi::_val), qi::_1)];
auto elevationParser = qi::int_[px::push_back(px::at_c<1>(qi::_val), qi::_1)];
auto frequencyElevationParser = qi::lit('[') >> frequencyParser >> qi::lit(':') >> elevationParser >> qi::lit(']');
auto gainsParser = qi::double_ % qi::lit(','); // Problem here where I want to parse vector rows
start = *(frequencyElevationParser >> gainsParser);
}
private:
qi::rule<It, Data(), Skipper> start;
};
int main() {
using It = std::string::const_iterator;
Data parsed;
bool ok = qi::phrase_parse(file1.begin(), file1.end(), grammar<It>(), qi::space, parsed);
return 0;
}
答案 0 :(得分:2)
本着我经常重复的口头禅Boost Spirit: "Semantic actions are evil"?的精神,我使用了一个特征,并将每个部分解析为Ast结构:
qi::rule<It, Data(), Skipper> start;
qi::rule<It, Ast::Data(), Skipper> section;
整个解析器不需要比这更复杂:
section = '[' >> int_ >> ':' >> int_ >> ']' >> double_ % ',';
start = *section;
让我们创建一个Ast结构,并且只适应它,而不是你给出的不切实际的数据类型:
namespace Ast {
struct Data {
int frequency;
int elevation;
std::vector<double> gains;
};
}
BOOST_FUSION_ADAPT_STRUCT(Ast::Data, frequency, elevation, gains)
现在,剩下要做的就是告诉Spirit如何将Data
视为Ast::Data
的容器:
namespace boost { namespace spirit { namespace traits {
template <> struct container_value<MyLib::Data> { using type = Ast::Data; };
static bool call(MyLib::Data& c, Ast::Data const& val) {
c.frequencies.push_back(val.frequency);
c.elevations.push_back(val.elevation);
c.gains.push_back(val.gains);
return true;
}
};
} } }
这简直太简单了。
<强> Live On Coliru 强>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
const std::string file1 = R"xx(
[70000000:45]
4, 5, 6, 7
[60000000:60]
1, 2, 3, 4
[80000:90]
4, 5, 6, 7, 8, 9
)xx";
namespace MyLib {
struct Data {
std::vector<int> frequencies;
std::vector<int> elevations;
std::vector<std::vector<double> > gains;
};
}
namespace Ast {
struct Data {
int frequency;
int elevation;
std::vector<double> gains;
};
}
namespace boost { namespace spirit { namespace traits {
template <> struct container_value<MyLib::Data> { using type = Ast::Data; };
template<> struct push_back_container<MyLib::Data, Ast::Data> {
static bool call(MyLib::Data& c, Ast::Data const& val) {
c.frequencies.push_back(val.frequency);
c.elevations.push_back(val.elevation);
c.gains.push_back(val.gains);
return true;
}
};
} } }
BOOST_FUSION_ADAPT_STRUCT(Ast::Data, frequency, elevation, gains)
namespace MyLib {
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, Data(), Skipper> {
grammar() : grammar::base_type(start) {
using namespace qi;
section = '[' >> int_ >> ':' >> int_ >> ']' >> double_ % ',';
start = *section;
}
private:
qi::rule<It, Data(), Skipper> start;
qi::rule<It, Ast::Data(), Skipper> section;
};
}
int main() {
using It = std::string::const_iterator;
MyLib::Data parsed;
bool ok = qi::phrase_parse(file1.begin(), file1.end(), MyLib::grammar<It>(), qi::space, parsed);
}
答案 1 :(得分:1)
使用X3,这变得更加简单:
#include <boost/spirit/home/x3.hpp>
#include <iostream>
namespace x3 = boost::spirit::x3;
const std::string file1 = R"xx(
[70000000:45]
4, 5, 6, 7
[60000000:60]
1, 2, 3, 4
[80000:90]
4, 5, 6, 7, 8, 9
)xx";
struct Data {
std::vector<int> frequencies;
std::vector<int> elevations;
std::vector<std::vector<double>> gains;
};
int main() {
Data parsed;
auto appender = [](auto& ctx)
{
Data& data = x3::_val(ctx);
auto& entry = x3::_attr(ctx);
data.frequencies.push_back(boost::fusion::at_c<0>(entry));
data.elevations.push_back(boost::fusion::at_c<1>(entry));
data.gains.emplace_back(std::move(boost::fusion::at_c<2>(entry)));
};
auto block = x3::rule<struct r_block, Data>{} =
(('[' >> x3::int_ >> ':' >> x3::int_ >> ']') >> (x3::double_ % ','))[appender];
auto it = file1.begin();
while (x3::phrase_parse(it, file1.end(), block, x3::space, parsed))
;
if (it != file1.end()) {
std::cout << "Not all input parsed" << std::endl;
}
assert(parsed.elevations.size() == parsed.frequencies.size()
&& parsed.frequencies.size() == parsed.gains.size());
for (int i = 0; i < parsed.frequencies.size(); ++i) {
std::cout << "frequency: " << parsed.frequencies[i] << std::endl;
std::cout << "elevations: " << parsed.elevations[i] << std::endl;
std::cout << "gains: ";
std::copy(parsed.gains[i].begin(), parsed.gains[i].end(), std::ostream_iterator<double>(std::cout, ","));
std::cout << std::endl;
}
return 0;
}
答案 2 :(得分:0)
我自己找到了解决方案
我在语法中为qi::rule
添加了std::vector<double>
,然后我用它来解析向量,将结果附加到结构中,就像其他字段一样。在更正后的代码下面:
#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;
namespace px = boost::phoenix;
const std::string file1 = R"xx(
[70000000:45]
4, 5, 6, 7
[60000000:60]
1, 2, 3, 4
[80000:90]
4, 5, 6, 7, 8, 9
)xx";
struct Data {
std::vector<int> frequencies;
std::vector<int> elevations;
std::vector<std::vector<double>> gains;
};
BOOST_FUSION_ADAPT_STRUCT(
Data,
(std::vector<int>, frequencies)
(std::vector<int>, elevations)
(std::vector<std::vector<double>>, gains)
)
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, Data(), Skipper> {
grammar() : grammar::base_type(start) {
auto frequencyParser = qi::int_[px::push_back(px::at_c<0>(qi::_val), qi::_1)];
auto elevationParser = qi::int_[px::push_back(px::at_c<1>(qi::_val), qi::_1)];
auto frequencyElevationParser = qi::lit('[') >> frequencyParser >> qi::lit(':') >> elevationParser >> qi::lit(']');
vectorParser = qi::double_ % qi::lit(',');
auto gainsParser = vectorParser[px::push_back(px::at_c<2>(qi::_val), qi::_1)];
start = *(frequencyElevationParser >> gainsParser);
}
private:
qi::rule<It, Data(), Skipper> start;
qi::rule<It, std::vector<double>(), Skipper> vectorParser;
};
int main() {
using It = std::string::const_iterator;
Data parsed;
bool ok = qi::phrase_parse(file1.begin(), file1.end(), grammar<It>(), qi::space, parsed);
return 0;
}