C++ 解析为多个向量成员

C++ 解析为多个向量成员,c++,boost-spirit,boost-spirit-qi,wavefront,C++,Boost Spirit,Boost Spirit Qi,Wavefront,我想递归地解析一个字符串,并将结果存储在一个结构中。我已经编写了一个可以处理一次迭代的解析器。输入的格式如下: 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>>元素修改为start=

我想递归地解析一个字符串,并将结果存储在一个结构中。我已经编写了一个可以处理一次迭代的解析器。输入的格式如下:

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>>元素
修改为
start=*(vertex>>元素)
,但这只会产生巨大的编译错误。这同样适用于
start=+(顶点>>元素)
。你知道我该如何修改规则吗

#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();
}
#包括
#包括
#包括
#包括“boost/spirit/include/qi.hpp”
#包括“推进/精神/包括/支持”iso8859_1.hpp
#包括“增强/融合/包含/适应结构hpp”
结构ElemParseData
{
向量顶点;
std::向量idx;
};
增强融合适应结构(
ElemParseData,
(标准::向量,顶点)
(标准::向量,idx)
)
booldoparse(ElemParseData和parseData,const std::string和data)
{
名称空间qi=boost::spirit::qi;
名称空间iso8859=boost::spirit::iso8859_1;
结构对象:qi::语法
{
objGram():objGram::基本类型(开始)
{
顶点=*('v'>>qi::double\u>>qi::double\u>>qi::double\u>>;
元素=*('f'>>qi::int\u>>qi::int\u>>qi::int\u>>;
开始=顶点>>元素;
}
qi::规则开始;
qi:规则顶点;
qi:规则元素;
}对象;
std::string::const_迭代器f=data.cbegin();
bool res=qi::phrase_parse(f,data.cend(),objGrammar,iso8859::space,parseData);
//打印分析器未处理的所有内容
有几种方法:)

  • 自定义属性特性
  • 使用语义动作也是如此
  • 语义动作中的所有细节
  • 1.自定义属性特征 最干净的是,IMO将用Spirit的自定义容器属性特性替换Fusion Sequence Adaption(
    BOOST\u Fusion\u ADAPT\u 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;
                }
            };
    }}}
    
    由于这些特性,Spirit将“只知道”如何插入
    ElemParseData
    。请参见

    2.使用语义动作也一样 您可以通过语义操作将其连接起来:

        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());
        }
    };
    
    注:

    • 这里有一个小小的优势,那就是复制值更少
    • 一个缺点是您失去了“原子性”(如果一行在第二个值之后无法解析,那么前两个值将被不可撤销地推送到
      ElemParseData
      成员中)
    旁注 读取循环中存在错误,请选择更简单的选项:

    std::filebuf fb;
    if (fb.open("parsetest.txt", std::ios::in))
    {
        ss << &fb;
        fb.close();
    }
    
    std::filebuf fb;
    if(fb.open(“parsetest.txt”,std::ios::in))
    {
    
    ss为什么需要递归解析?看起来迭代会更简单。我想要递归,因为我不知道“v”和“f”重复多少次。“v”和“f”总是紧跟在一起,但这种组合可能会发生无限长的时间(理论上)迭代仍然符合要求:它是一个顶点或索引的简单循环:)当然,请看我的答案!这正是我想要的。谢谢:)。这些特性看起来很有趣。我将进一步研究这些特性,看看我能用它做些什么。关于你的旁注:你能告诉我bug是什么吗?我真的看不出我有什么问题t、 。这是关于char cast的,不是吗?…@krienlinenbank我刚刚添加了另外两种方法,用于降低封装级别。后者可能具有一些有趣的性能特征。但是,请参见此:和选项3。继续。关于错误:这是一个正在读取的伪字符EOF@sehe你的答案一直都是正确的吃,但最近它们简直太神奇了。这是一个关于精神的伟大学习资源。@cv_和_他确实,在我问问题之前,我也成功地做到了这一点,但额外的工作连接向量似乎不是“正确”的方式。。
    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;
    
    std::filebuf fb;
    if (fb.open("parsetest.txt", std::ios::in))
    {
        ss << &fb;
        fb.close();
    }