C++ Spirit X3:具有内部状态的解析器
我想高效地解析类似CSV的大型文件,我在运行时获取这些文件的列顺序。使用Spirit Qi,我将使用C++ Spirit X3:具有内部状态的解析器,c++,c++17,boost-spirit,boost-spirit-x3,C++,C++17,Boost Spirit,Boost Spirit X3,我想高效地解析类似CSV的大型文件,我在运行时获取这些文件的列顺序。使用Spirit Qi,我将使用lazy辅助解析器解析每个字段,该解析器将在运行时选择要应用于每个列的特定于列的解析器。但X3似乎没有惰性(尽管如此)。在阅读了这里的建议之后,我决定编写一个自定义解析器 结果非常好,但现在我注意到我并不需要将pos变量暴露在自定义解析器本身之外的任何地方。我尝试将其放入自定义解析器本身,并开始出现编译器错误,指出列\u值\u解析器对象是只读的。我是否可以将pos放入解析器结构中 获取编译时错误的
lazy
辅助解析器解析每个字段,该解析器将在运行时选择要应用于每个列的特定于列的解析器。但X3似乎没有惰性(尽管如此)。在阅读了这里的建议之后,我决定编写一个自定义解析器
结果非常好,但现在我注意到我并不需要将pos
变量暴露在自定义解析器本身之外的任何地方。我尝试将其放入自定义解析器本身,并开始出现编译器错误,指出列\u值\u解析器
对象是只读的。我是否可以将pos
放入解析器结构中
获取编译时错误的简化代码,其中注释掉了我工作版本的部分内容:
#include <iostream>
#include <variant>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/support.hpp>
namespace helpers {
// https://bitbashing.io/std-visit.html
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
}
auto const unquoted_text_field = *(boost::spirit::x3::char_ - ',' - boost::spirit::x3::eol);
struct text { };
struct integer { };
struct real { };
struct skip { };
typedef std::variant<text, integer, real, skip> column_variant;
struct column_value_parser : boost::spirit::x3::parser<column_value_parser> {
typedef boost::spirit::unused_type attribute_type;
std::vector<column_variant>& columns;
// size_t& pos;
size_t pos;
// column_value_parser(std::vector<column_variant>& columns, size_t& pos)
column_value_parser(std::vector<column_variant>& columns)
: columns(columns)
// , pos(pos)
, pos(0)
{ }
template<typename It, typename Ctx, typename Other, typename Attr>
bool parse(It& f, It l, Ctx& ctx, Other const& other, Attr& attr) const {
auto const saved_f = f;
bool successful = false;
visit(
helpers::overloaded {
[&](skip const&) {
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::omit[unquoted_text_field]);
},
[&](text& c) {
std::string value;
successful = boost::spirit::x3::parse(f, l, unquoted_text_field, value);
if(successful) {
std::cout << "Text: " << value << '\n';
}
},
[&](integer& c) {
int value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::int_, value);
if(successful) {
std::cout << "Integer: " << value << '\n';
}
},
[&](real& c) {
double value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::double_, value);
if(successful) {
std::cout << "Real: " << value << '\n';
}
}
},
columns[pos]);
if(successful) {
pos = (pos + 1) % columns.size();
return true;
} else {
f = saved_f;
return false;
}
}
};
int main(int argc, char *argv[])
{
std::string input = "Hello,1,13.7,XXX\nWorld,2,1e3,YYY";
// Comes from external source.
std::vector<column_variant> columns = {text{}, integer{}, real{}, skip{}};
size_t pos = 0;
boost::spirit::x3::parse(
input.begin(), input.end(),
// (column_value_parser(columns, pos) % ',') % boost::spirit::x3::eol);
(column_value_parser(columns) % ',') % boost::spirit::x3::eol);
}
#包括
#包括
#包括
#包括
命名空间帮助程序{
// https://bitbashing.io/std-visit.html
模板结构重载:Ts..{using Ts::operator()…;};
模板重载(Ts…)->重载;
}
auto const unquoted_text_field=*(boost::spirit::x3::char_-','-boost::spirit::x3::eol);
结构文本{};
结构整数{};
结构实数{};
结构跳过{};
typedef std::variant列_variant;
结构列\u值\u解析器:boost::spirit::x3::parser{
typedef boost::spirit::unused_type属性_type;
std::向量和列;
//尺寸和位置;
尺寸和位置;
//列值解析器(标准::向量和列、大小和位置)
列值解析器(std::vector&columns)
:列(列)
//,pos(pos)
,位置(0)
{ }
模板
布尔解析(It&f、It-l、Ctx&Ctx、其他常量和其他、属性和属性)常量{
保存的自动常数f=f;
bool成功=错误;
拜访(
助手::重载{
[&](跳过常数&){
successful=boost::spirit::x3::parse(f,l,boost::spirit::x3::省略[unquoted_text_field]);
},
[&](文本和c){
std::字符串值;
successful=boost::spirit::x3::parse(f,l,无引号的文本字段,值);
如果(成功){
标准::cout三个答案:
最简单的修复方法是使pos
amutable
成员
X3的核心答案是X3::with
功能成分
1.使pos
可变
#include <iostream>
#include <variant>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/support.hpp>
namespace helpers {
// https://bitbashing.io/std-visit.html
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
}
auto const unquoted_text_field = *(boost::spirit::x3::char_ - ',' - boost::spirit::x3::eol);
struct text { };
struct integer { };
struct real { };
struct skip { };
typedef std::variant<text, integer, real, skip> column_variant;
struct column_value_parser : boost::spirit::x3::parser<column_value_parser> {
typedef boost::spirit::unused_type attribute_type;
std::vector<column_variant>& columns;
size_t mutable pos = 0;
struct pos_tag;
column_value_parser(std::vector<column_variant>& columns)
: columns(columns)
{ }
template<typename It, typename Ctx, typename Other, typename Attr>
bool parse(It& f, It l, Ctx& /*ctx*/, Other const& /*other*/, Attr& /*attr*/) const {
auto const saved_f = f;
bool successful = false;
visit(
helpers::overloaded {
[&](skip const&) {
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::omit[unquoted_text_field]);
},
[&](text&) {
std::string value;
successful = boost::spirit::x3::parse(f, l, unquoted_text_field, value);
if(successful) {
std::cout << "Text: " << value << '\n';
}
},
[&](integer&) {
int value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::int_, value);
if(successful) {
std::cout << "Integer: " << value << '\n';
}
},
[&](real&) {
double value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::double_, value);
if(successful) {
std::cout << "Real: " << value << '\n';
}
}
},
columns[pos]);
if(successful) {
pos = (pos + 1) % columns.size();
return true;
} else {
f = saved_f;
return false;
}
}
};
int main() {
std::string input = "Hello,1,13.7,XXX\nWorld,2,1e3,YYY";
std::vector<column_variant> columns = {text{}, integer{}, real{}, skip{}};
boost::spirit::x3::parse(
input.begin(), input.end(),
(column_value_parser(columns) % ',') % boost::spirit::x3::eol);
}
#include <iostream>
#include <variant>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/support.hpp>
namespace helpers {
// https://bitbashing.io/std-visit.html
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
}
auto const unquoted_text_field = *(boost::spirit::x3::char_ - ',' - boost::spirit::x3::eol);
struct text { };
struct integer { };
struct real { };
struct skip { };
typedef std::variant<text, integer, real, skip> column_variant;
struct column_value_parser : boost::spirit::x3::parser<column_value_parser> {
typedef boost::spirit::unused_type attribute_type;
std::vector<column_variant>& columns;
column_value_parser(std::vector<column_variant>& columns)
: columns(columns)
{ }
template<typename It, typename Ctx, typename Other, typename Attr>
bool parse(It& f, It l, Ctx const& ctx, Other const& /*other*/, Attr& /*attr*/) const {
auto const saved_f = f;
bool successful = false;
size_t& pos = boost::spirit::x3::get<pos_tag>(ctx).value;
visit(
helpers::overloaded {
[&](skip const&) {
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::omit[unquoted_text_field]);
},
[&](text&) {
std::string value;
successful = boost::spirit::x3::parse(f, l, unquoted_text_field, value);
if(successful) {
std::cout << "Text: " << value << '\n';
}
},
[&](integer&) {
int value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::int_, value);
if(successful) {
std::cout << "Integer: " << value << '\n';
}
},
[&](real&) {
double value;
successful = boost::spirit::x3::parse(f, l, boost::spirit::x3::double_, value);
if(successful) {
std::cout << "Real: " << value << '\n';
}
}
},
columns[pos]);
if(successful) {
pos = (pos + 1) % columns.size();
return true;
} else {
f = saved_f;
return false;
}
}
template <typename T>
struct Mutable { T mutable value; };
struct pos_tag;
auto invoke() const {
return boost::spirit::x3::with<pos_tag>(Mutable<size_t>{}) [ *this ];
}
};
int main() {
std::string input = "Hello,1,13.7,XXX\nWorld,2,1e3,YYY";
std::vector<column_variant> columns = {text{}, integer{}, real{}, skip{}};
column_value_parser p(columns);
boost::spirit::x3::parse(
input.begin(), input.end(),
(p.invoke() % ',') % boost::spirit::x3::eol);
}
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;
namespace CSV {
struct text { };
struct integer { };
struct real { };
struct skip { };
auto const unquoted_text_field = *~x3::char_(",\n");
static inline auto as_parser(skip) { return x3::omit[unquoted_text_field]; }
static inline auto as_parser(text) { return unquoted_text_field; }
static inline auto as_parser(integer) { return x3::int_; }
static inline auto as_parser(real) { return x3::double_; }
template <typename... Spec>
static inline auto line_parser(Spec... spec) {
auto delim = ',' | &(x3::eoi | x3::eol);
return ((as_parser(spec) >> delim) >> ... >> x3::eps);
}
template <typename... Spec> static inline auto csv_parser(Spec... spec) {
return line_parser(spec...) % x3::eol;
}
}
#include <iostream>
#include <iomanip>
using namespace CSV;
int main() {
std::string const input = "Hello,1,13.7,XXX\nWorld,2,1e3,YYY";
auto f = begin(input), l = end(input);
auto p = csv_parser(text{}, integer{}, real{}, skip{});
if (parse(f, l, p)) {
std::cout << "Parsed\n";
} else {
std::cout << "Failed\n";
}
if (f!=l) {
std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n";
}
}
#包括
#包括
#包括
#包括
命名空间帮助程序{
// https://bitbashing.io/std-visit.html
模板结构重载:Ts..{using Ts::operator()…;};
模板重载(Ts…)->重载;
}
auto const unquoted_text_field=*(boost::spirit::x3::char_-','-boost::spirit::x3::eol);
结构文本{};
结构整数{};
结构实数{};
结构跳过{};
typedef std::variant列_variant;
结构列\u值\u解析器:boost::spirit::x3::parser{
typedef boost::spirit::unused_type属性_type;
std::向量和列;
大小可变位置=0;
结构位置标签;
列值解析器(std::vector&columns)
:列(列)
{ }
模板
布尔解析(It&f,It-l,Ctx&/*Ctx*/,其他常量&/*Other*/,Attr&/*Attr*/)常量{
保存的自动常数f=f;
bool成功=错误;
拜访(
助手::重载{
[&](跳过常数&){
successful=boost::spirit::x3::parse(f,l,boost::spirit::x3::省略[unquoted_text_field]);
},
[&](文本&){
std::字符串值;
successful=boost::spirit::x3::parse(f,l,无引号的文本字段,值);
如果(成功){
std::我可以考虑让它可变,但我认为,x3
构成解析器有一个很好的理由。如果你说它可以,那就太好了!这是一个爱好项目,所以硬核方法更好(:我将等待第三个选择,尽管我想知道——我希望按需构建解析器不会让它变慢?你看到警告了吗?是的,x3的构成有很好的理由。主要是状态和静态逻辑的分离,这意味着编译器在优化方面有很好的处理能力。按需生成是否会让它变慢,完全取决于何时生成nerate和你重复使用的频率。出于好奇,所以我以后炮制的任何样本都可以在现实生活中应用,目的是什么?你现在只是打印和“扔”去掉属性。我假设有一个目标数据结构。如果没有要求,这是我建议的最简单的方法:-在没有已知目标的情况下,我将避免解析为适应的结构或解析为基于真正动态CSV规范的通用容器。更新了答案。另请参见vs中的这个旧CSV示例上下文是这样的。请注意,房间里的大象:为什么不导出,比如说,XML或在数据库上工作而不是转储?@sehe,哦,我希望它是以一种更理智的方式完成的……作为一个业余爱好项目,我只说服数据集的所有者给我他们可以用很少的技术技能轻松检查的东西。这还是更好的我会首先解析自定义AST节点(例如,在共享/映射内存容器中),并从中实现所有逻辑。