使用 Boost Spirit 为类似矩阵的输入编写解析器
Writing a parser for a matrix-like input with Boost Spirit
我正在尝试编写一个能够接受MATRIX.{variableName} = [1,2,3;4,5,6]
形式输入的解析器,其中矩阵的表示(在这种情况下为 2x3 矩阵)有点像 MATLAB 的格式(分号表示新行)。
最初的想法是将输入保存在 2d std 向量中,以便进一步处理数据。这是我第一次编写解析器,我对 Spirit 框架有些一无所知。
我目前(不是那么直观)的解决方案是让输入类似于MATRIX (2,3) = [1,2,3,4,5,6]
表示与上述相同的矩阵,并将数据保存在一维向量中,并利用行和列数据稍后处理它(我相信有点像 Eigen 的动态矩阵实现)。
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;
namespace fusion = boost::fusion;
template <typename Iterator>
bool parse_matrix(Iterator first, Iterator last, unsigned& rows, unsigned& cols, std::vector<double>& vals)
{
using qi::double_;
using qi::uint_;
using qi::_1;
using qi::lit;
using qi::phrase_parse;
using ascii::space;
using phoenix::push_back;
double rN = 0.0;
double iN = 0.0;
unsigned i=0;
rows = 0, cols = 0;
bool r = phrase_parse(first, last,
// Begin grammar
(
lit("MATRIX") >> '(' >> uint_[phoenix::ref(rows) = _1] >> ',' >> uint_[phoenix::ref(cols) = _1] >> ')' >> '='
>> '[' >> double_[push_back(phoenix::ref(vals),_1)]
>> *(',' >> double_[push_back(phoenix::ref(vals),_1)]) >> ']'
// | double_[ref(rN) = _1]
),
// End grammar
space);
if (!r || first != last) // fail if we did not get a full match
return false;
if (vals.size() != (rows*cols))
return false;
// c = std::complex<double>(rN, iN);
return r;
}
}
我在想也许可以在解析某些字符(如分号)时调用诸如将std::vector<double>
附加到std::vector<std::vector<double> >
之类的函数。这可能吗?或者我如何实际实施我最初的想法?
我建议:
-
不使用语义操作进行属性传播。你可以用它来添加验证标准(见Boost Spirit:"语义行为是邪恶的"?
-
使用自动属性传播,因此不必传递引用
-
除非有迫切的理由,否则不会在解析期间进行验证。
然后,最小可行的解析器变为:
住在科里鲁
#include <boost/spirit/include/qi.hpp>
using It = boost::spirit::istream_iterator;
using Row = std::vector<double>;
using Mat = std::vector<Row>;
int main() {
It f(std::cin>>std::noskipws), l;
Mat matrix;
std::string name;
{
using namespace boost::spirit::qi;
rule<It, std::string()> varname_ = char_("a-zA-Z_") >> *char_("a-zA-Z0-9_");
if (phrase_parse(f, l,
lit("MATRIX") >> '.' >> '{' >> varname_ >> '}' >> '='
>> '[' >> (int_ % ',' % ';') >> ']',
space, name, matrix))
{
std::cout << "Parsed: variabled named '" << name << "' [";
for(auto& row : matrix)
std::copy(row.begin(), row.end(), std::ostream_iterator<double>(std::cout<<"nt",", "));
std::cout << "n]n";
} else {
std::cout << "Parse failedn";
}
}
if (f!=l)
std::cout << "Remaining input: '" << std::string(f,l) << "'n";
}
可以看到打印以下输入"MATRIX.{variable_name}=[1,2,3;4,5,6]"
输出:
Parsed: variabled named 'variable_name' [
1, 2, 3,
4, 5, 6,
]
如果您想尽早捕获不一致的行长度,请参阅例如以下答案:
- 提升::
- 精神::QI解析器:解析元素的索引(当你知道前面的维度时) 使用
- boost::spirit 读取 2D 数组C++(将检查添加为后置条件,并在 UPDATE 下使用语义操作进行早期检查)
您需要将表达式分解为如下所示的内容:
rows: DOUBLE | rows ',' DOUBLE
columns: rows | rows ';' rows
matrix: IDENTIFIER '.' '{' IDENTIFIER '}' '=' '[' columns ']'
现在,您可以对行进行vector<double>
。列的vector< vector<double> >
,并将结果保存在矩阵中。
当然,当您在矩阵中获得一列时,您应该检查所有行的大小是否相同。它不是必需的,但显然像 [1,2;3,4,5]
这样的矩阵是无效的,即使语法允许它。
- 在C++程序中输入的文本文件将不起作用,除非文本被复制和粘贴
- 2D数组来自文本输入,中间有空格
- 如何使用 < 和 > 命令获取 c++ 中的输入和输出?
- 检查输入是否不是整数或数字
- 正在尝试了解输入验证循环
- 读取文件并输入到矢量中
- C++如何通过用户输入删除列表元素
- 用c++从输入文件中读取另一行
- 读取文件的最后一行并输入到链接列表时出错
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- 如何使用用户输入在C++中正确填充2D数组
- 使用 Spirit x3,如何控制在每个不同的输入上调用哪个解析器?
- Spirit X3,如何在非ASCII输入上失败
- Spirit Qi用简单的C风格结构化输入解析问题
- C++Spirit Boost:将输入迭代器转换为前向迭代器
- boost::spirit:用于多维输入的迭代器和解析
- boost.spirit:对不完整的输入流采取行动
- 使用 Boost Spirit 为类似矩阵的输入编写解析器
- Boost Spirit:解析输入的一段
- 使用boost::spirit::qi解析输入的CSV文件