我想递归地解析一个字符串并将结果存储在一个结构中。我已经编写了一个可以处理一次迭代的解析器。输入格式如下:
v 1.5 2.0 2.5
v 3.0 3.5 4.0
f 1 2 3
f 4 5 6
v 4.5 5.0 5.5
v 6.0 6.5 7.0
f 7 8 9
f 10 11 12
问题是它只解析前 4 行,它在遇到第三个“v”时停止。完整代码如下。我如何修改此代码,以便它也将输入的其余部分解析为同一结构?我尝试将开始规则从 start = vertex >> elements 修改为 start = *(vertex >> elements),但这只会产生巨大的编译错误。 start = +(vertex >> elements) 也是如此。我应该如何修改规则有什么想法吗?
#include <iostream>
#include <sstream>
#include <fstream>
#include "boost/spirit/include/qi.hpp"
#include "boost/spirit/include/support_iso8859_1.hpp"
#include "boost/fusion/include/adapt_struct.hpp"
struct ElemParseData
{
std::vector<float> verts;
std::vector<unsigned int> idx;
};
BOOST_FUSION_ADAPT_STRUCT(
ElemParseData,
(std::vector<float>, verts)
(std::vector<unsigned int>, idx)
)
bool doParse( ElemParseData &parseData, const std::string &data )
{
namespace qi = boost::spirit::qi;
namespace iso8859 = boost::spirit::iso8859_1;
struct objGram : qi::grammar<std::string::const_iterator, ElemParseData(), iso8859::space_type>
{
objGram() : objGram::base_type(start)
{
vertex = *('v' >> qi::double_ >> qi::double_ >> qi::double_);
elements = *('f' >> qi::int_ >> qi::int_ >> qi::int_);
start = vertex >> elements;
}
qi::rule<std::string::const_iterator, ElemParseData(), iso8859::space_type> start;
qi::rule<std::string::const_iterator, std::vector<float>(), iso8859::space_type> vertex;
qi::rule<std::string::const_iterator, std::vector<unsigned int>(), iso8859::space_type> elements;
} objGrammar;
std::string::const_iterator f = data.cbegin();
bool res = qi::phrase_parse( f, data.cend(), objGrammar, iso8859::space, parseData );
// print everything that hasn't been processed by the parser
std::cout << "#### Trail ####" << std::endl;
std::cout << std::string(f, data.cend()) << std::endl;
return res;
}
int main( int argc, char* argv[] )
{
std::stringstream ss;
std::filebuf fb;
if ( fb.open("parsetest.txt", std::ios::in) )
{
std::istream is(&fb);
while (is)
ss << char(is.get());
fb.close();
}
ElemParseData parseData;
bool res = doParse( parseData, ss.str() );
// print results
std::cout << std::endl << "Parsing result: " << res << std::endl;
std::cout << "---######### ResultData #########---" << std::endl;
std::cout << "---- Begin vertex data ----" << std::endl;
std::vector<float>::iterator it;
for ( it = parseData.verts.begin(); it != parseData.verts.end(); ++it )
std::cout << *it << std::endl;
std::cout << "---- End vertex data ----" << std::endl;
std::cout << std::endl;
std::cout << "---- Begin index data ----" << std::endl;
std::vector<unsigned int>::iterator idxIt;
for ( idxIt = parseData.idx.begin(); idxIt != parseData.idx.end(); ++idxIt )
std::cout << *idxIt << std::endl;
std::cout << "---- End index data ----" << std::endl;
std::cout << "Press enter to exit" << std::endl;
std::cin.get();
}
P.S.:如果需要,编译错误可以找here .
编辑:我正在尝试编写 Wavefront .OBJ 解析器。此处提供的输入只是对我的问题的简化。
最佳答案
有几种方法:)
最干净的,IMO 将用 Spirit 的自定义容器属性特征替换 Fusion Sequence Adaptation (BOOST_FUSION_ADAPT_STRUCT):
namespace boost { namespace spirit { namespace traits {
template<>
struct is_container<ElemParseData, void> : mpl::true_ { };
template<>
struct container_value<ElemParseData, void> {
typedef boost::variant<float, unsigned int> type;
};
template <>
struct push_back_container<ElemParseData, std::vector<float>, void> {
static bool call(ElemParseData& c, std::vector<float> const& val) {
c.verts.insert(c.verts.end(), val.begin(), val.end());
return true;
}
};
template <>
struct push_back_container<ElemParseData, std::vector<unsigned int>, void> {
static bool call(ElemParseData& c, std::vector<unsigned int> const& val) {
c.idx.insert(c.idx.end(), val.begin(), val.end());
return true;
}
};
}}}
如果不更改语法,这只会产生相同的效果。但是,现在您可以修改解析器以获得所需的语法:
vertex = 'v' >> qi::double_ >> qi::double_ >> qi::double_;
elements = 'f' >> qi::int_ >> qi::int_ >> qi::int_;
start = *(vertex | elements);
并且由于这些特征,Spirit 将“只知道”如何插入 ElemParseData。见<强>live on Coliru
您可以将其连接到语义操作中:
start = *(
vertex [phx::bind(insert, _val, _1)]
| elements [phx::bind(insert, _val, _1)]
);
使用 insert 类型的成员 inserter:
struct inserter {
template <typename,typename> struct result { typedef void type; };
template <typename Attr, typename Vec>
void operator()(Attr& attr, Vec const& v) const { dispatch(attr, v); }
private:
static void dispatch(ElemParseData& data, std::vector<float> vertices) {
data.verts.insert(data.verts.end(), vertices.begin(), vertices.end());
}
static void dispatch(ElemParseData& data, std::vector<unsigned int> indices) {
data.idx.insert(data.idx.end(), indices.begin(), indices.end());
}
};
这看起来大体相同,而且作用相同: live on Coliru
这是唯一不需要任何管道的解决方案,除了可能包含 boost/spirit/include/phoenix.hpp:
struct objGram : qi::grammar<std::string::const_iterator, ElemParseData(), iso8859::space_type>
{
objGram() : objGram::base_type(start)
{
using namespace qi;
auto add_vertex = phx::push_back(phx::bind(&ElemParseData::verts, _r1), _1);
auto add_index = phx::push_back(phx::bind(&ElemParseData::idx, _r1), _1);
vertex = 'v' >> double_ [add_vertex] >> double_ [add_vertex] >> double_ [add_vertex];
elements = 'f' >> int_ [add_index] >> int_ [add_index] >> int_ [add_index] ;
start = *(vertex(_val) | elements(_val));
}
qi::rule<std::string::const_iterator, ElemParseData(), iso8859::space_type> start;
qi::rule<std::string::const_iterator, void(ElemParseData&), iso8859::space_type> vertex, elements;
} objGrammar;
注意:
ElemParseData 成员不可撤销)。强>Side note
There is a bug in the read loop, prefer the simpler options:
std::filebuf fb; if (fb.open("parsetest.txt", std::ios::in)) { ss << &fb; fb.close(); }Or consider
boost::spirit::istream_iterator
关于c++ - 解析为多个 vector 成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17661026/
我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我正在使用ruby1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2
我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这
对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs