Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用Boost.Spirit解析具有混合数据类型的OBJ文件?_C++_Boost_Boost Spirit_Wavefront - Fatal编程技术网

C++ 使用Boost.Spirit解析具有混合数据类型的OBJ文件?

C++ 使用Boost.Spirit解析具有混合数据类型的OBJ文件?,c++,boost,boost-spirit,wavefront,C++,Boost,Boost Spirit,Wavefront,我确实有一个如下所示的OBJ文件: # This file uses centimeters as units for non-parametric coordinates. # first element v -0.017050 -0.017928 0.005579 v -0.014504 -0.017928 0.010577 . . v -0.000000 -0.017928 0.017967 vt 0.397581 0.004762 vt 0.397544 0.034263

我确实有一个如下所示的OBJ文件:

# This file uses centimeters as units for non-parametric coordinates.

# first element
v -0.017050 -0.017928 0.005579
v -0.014504 -0.017928 0.010577
   .
   .
v -0.000000 -0.017928 0.017967

vt 0.397581 0.004762
vt 0.397544 0.034263
   .
   .
vt 0.397507 0.063764

vn -0.951057 0.000000 0.309016
vn -0.809017 0.000000 0.587785
   .
   .
vn -0.587785 0.000000 0.809017

f 1/1/1 2/2/2 21/4/3
f 21/4/3 2/2/2 22/3/4
   .
   .
f 3/5/5 4/7/7 23/6/6


# second element
v -0.014504 0.017928 -0.010499
v -0.017050 0.017928 -0.005501
   .
   .
v -0.000000 0.017928 0.000039

vt 0.063001 0.262615
vt 0.073837 0.268136
   .
   .
vt 0.089861 0.299584

vn 0.000000 1.000000 -0.000002
vn 0.000000 1.000000 -0.000002
   .
   .
vn 0.000000 1.000000 -0.000000

f 36/80/78 37/81/79 42/66/64
f 37/81/79 38/82/80 42/66/64
   .
   .
f 40/84/82 21/64/62 42/66/64

# third element
   .
   .
etc.
#include <iostream>
#include <iomanip>
int main() {
    std::ifstream ifs("input.txt");
    boost::spirit::istream_iterator f(ifs >> std::noskipws), l;

    OBJ data;
    if (x3::parse(f, l, x3::with<OBJ>(data) [ Parser::OBJ ])) {
        std::cout << "Yay, " << data.elements.size() << " elements\n";

        for (auto& el : data.elements) {
            dump(el);
        }
    } else {
        std::cout << "Failed\n";
    }

    if (f!=l) {
        std::cout << "Remaining unparsed: " << std::quoted(std::string(f,l)) << "\n";
    }
}
该文件包含四种不同类型的记录,每种记录以
v
vt
vn
f
开头。 我想知道在以下条件下如何使用boost spirit解析这样的文件:

int main(int argc, char **argv) {

    std::vector<double> positions;
    std::vector<double> texcoords;
    std::vector<double> normals;
    std::vector<uint32_t> faces;


    // 1- store each record begins with 'v' into 'positions'
    // 2- store each record begins with 'vt' into 'texcoords'
    // 3- store each record begins with 'vn' into 'normals'
    // 4- store each record begins with 'f' into 'faces'.  The integers are separated either with '/' or a blank-space (Each record with three groups, each group with three integers)
    // 5- ignore each record begins with '#'
    // 6- ignoring all empty lines

    return 0;
}
int main(int argc,char**argv){
std::向量位置;
std::向量texcoords;
std::向量法线;
向量面;
//1-将以“v”开头的每条记录存储到“位置”
//2-将以“vt”开头的每条记录存储到“texcoords”中
//3-将以“vn”开头的每条记录存储到“normals”中
//4-将以“f”开头的每条记录存储到“faces”中。整数用“/”或空格分隔(每条记录有三个组,每组有三个整数)
//5-忽略以“#”开头的每条记录
//6-忽略所有空行
返回0;
}

如何以及使用boost spirit最有效的方法是什么?

好的,因此我开始在spirit X3中阐述这一点,作为一种改变

AST 一如往常,第一件事是第一位的。我认为我们可以对数据结构更精确一些,以匹配数据本身

这将使它更加有用,并且不容易出错

using Position = std::tuple<double, double, double>;
using Coords   = std::tuple<double, double>;
using Normal   = std::tuple<double, double, double>;
//using Face3    = std::array<uint32_t, 3>;
//using Face9    = std::array<Face3, 3>; // 9
using Face9    = std::vector<uint32_t>; // hmmm

struct Element {
    std::vector<Position> positions;
    std::vector<Coords>   texcoords;
    std::vector<Normal>   normals;
    std::vector<Face9>    faces;
};

struct OBJ {
    std::vector<Element> elements;
};
我们不关心注释/空白,因为我们将使用一个skipper:

// 5- ignore each record begins with '#'
// 6- ignoring all empty lines
auto skipper = blank | '#' >> *(char_ - eol) >> (eol|eoi);
现在,让我们跳到最后一个阶段:主解析器规则:

auto OBJ = *skip(skipper) [
    *eol >> &pos [ new_element ] >>
    lines_of(pos,     &Element::positions) >>
    lines_of(tex,     &Element::texcoords) >>
    lines_of(normals, &Element::normals) >>
    lines_of(faces,   &Element::faces)
];
正如您所看到的,我再次假设了一些限制(因为示例文件中的注释表明出现了单独的元素,并且各个部分按顺序出现)

要分析重复的行,我们有以下工厂:

auto lines_of = [](auto p, auto member) {
    return *(p [ push(member) ] >> (eoi|+eol));
};
push
操作是按功能生成的:

auto push = [](auto member) {
    return [member](auto& ctx) {
        auto& data = get<OBJ>(ctx);
        auto& v = std::invoke(member, data.elements.back());
        v.push_back(std::move(_attr(ctx)));
    };
};
就这些。对于给定的输入,打印:

Yay, 2 elements
好处:调试输出 如果您有可用的
{fmt}
,请使用
-DHAVE_fmt
进行编译,以获得漂亮的输出:

#ifdef HAVE_FMT
#include <fmt/printf.h>
#include <fmt/ranges.h>
void dump(Element const& el) {
    auto& [pos,tex,nrm,fac] = el;
    fmt::print("positions: {}\n"
            "texcoords: {}\n"
            "normals: {}\n"
            "faces: {}\n\n", pos, tex, nrm, fac);
}
#else
void dump(Element const&) { }
#endif
完整列表

//#define HAVE_FMT
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/adapted/std_array.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <fstream>

using Position = std::tuple<double, double, double>;
using Coords   = std::tuple<double, double>;
using Normal   = std::tuple<double, double, double>;
//using Face3    = std::array<uint32_t, 3>;
//using Face9    = std::array<Face3, 3>; // 9
using Face9    = std::vector<uint32_t>; // hmmm

struct Element {
    std::vector<Position> positions;
    std::vector<Coords>   texcoords;
    std::vector<Normal>   normals;
    std::vector<Face9>    faces;
};

struct OBJ {
    std::vector<Element> elements;
};

namespace x3 = boost::spirit::x3;

namespace Parser {
    using namespace x3;

    auto new_element = [](auto& ctx) {
        auto& data = get<OBJ>(ctx);
        data.elements.emplace_back();
    };

    // 1- store each record begins with 'v' into 'positions'
    auto pos
        = rule<struct _pos, Position> {"pos"}
        = "v" >> double_ >> double_ >> double_;

    // 2- store each record begins with 'vt' into 'texcoords'
    auto tex
        = rule<struct _tex, Coords> {"tex"}
        = "vt" >> double_ >> double_;

    // 3- store each record begins with 'vn' into 'normals'
    auto normals
        = rule<struct _norm, Normal> {"normal"}
        = "vn" >> double_ >> double_ >> double_;

    // 4- store each record begins with 'f' into 'faces'.
    //    The integers are separated either with '/' or a blank-space (Each
    //    record with three groups, each group with three integers)

    auto face3
        = rule<struct _face3, Face9> {"face3"}
        = uint_ >> '/' >> uint_ >> '/' >> uint_;
    auto faces
        = rule<struct _faces, Face9> {"faces"}
        = "f" >> repeat(3) [ face3 ];

    // 5- ignore each record begins with '#'
    // 6- ignoring all empty lines
    auto skipper = blank | '#' >> *(char_ - eol) >> (eol|eoi);

    auto push = [](auto member) {
        return [member](auto& ctx) {
            auto& data = get<OBJ>(ctx);
            auto& v = std::invoke(member, data.elements.back());
            v.push_back(std::move(_attr(ctx)));
        };
    };

    auto lines_of = [](auto p, auto member) {
        return *(p [ push(member) ] >> (eoi|+eol));
    };

    auto OBJ = *skip(skipper) [
        *eol >> &pos [ new_element ] >>
        lines_of(pos,     &Element::positions) >>
        lines_of(tex,     &Element::texcoords) >>
        lines_of(normals, &Element::normals) >>
        lines_of(faces,   &Element::faces)
    ];
}

#ifdef HAVE_FMT
#include <fmt/printf.h>
#include <fmt/ranges.h>
void dump(Element const& el) {
    auto& [pos,tex,nrm,fac] = el;
    fmt::print("positions: {}\n"
            "texcoords: {}\n"
            "normals: {}\n"
            "faces: {}\n\n", pos, tex, nrm, fac);
}
#else
void dump(Element const&) { }
#endif

#include <iostream>
#include <iomanip>
int main() {
    std::ifstream ifs("input.txt");
    boost::spirit::istream_iterator f(ifs >> std::noskipws), l;

    OBJ data;
    if (x3::parse(f, l, x3::with<OBJ>(data) [ Parser::OBJ ])) {
        std::cout << "Yay, " << data.elements.size() << " elements\n";

        for (auto& el : data.elements) {
            dump(el);
        }
    } else {
        std::cout << "Failed\n";
    }

    if (f!=l) {
        std::cout << "Remaining unparsed: " << std::quoted(std::string(f,l)) << "\n";
    }
}
/#定义有(FMT)
//#定义BOOST_SPIRIT_X3_调试
#包括
#包括
#包括
#包括
#包括
使用Position=std::tuple;
使用Coords=std::tuple;
使用Normal=std::tuple;
//使用Face3=std::array;
//使用Face9=std::array;//9
使用Face9=std::vector;//六羟甲基三聚氰胺六甲醚
结构元素{
std::向量位置;
std::向量texcoords;
std::向量法线;
向量面;
};
结构对象{
std::向量元素;
};
名称空间x3=boost::spirit::x3;
名称空间分析器{
使用名称空间x3;
自动新建元素=[](自动和ctx){
自动和数据=获取(ctx);
data.elements.emplace_back();
};
//1-将以“v”开头的每条记录存储到“位置”
自动pos机
=规则{“pos”}
=“v”>>双刀>>双刀>>双刀;
//2-将以“vt”开头的每条记录存储到“texcoords”中
自动纺织
=规则{“tex”}
=“vt”>>双精度>>双精度;
//3-将以“vn”开头的每条记录存储到“normals”中
自动法线
=规则{“正常”}
=“vn”>>双精度>>双精度>>双精度;
//4-将以“f”开头的每条记录存储到“faces”中。
//整数用“/”或空格分隔(每个
//记录三组,每组三个整数)
自动脸3
=规则{“face3”}
=uint_>>'/'>>uint_>>'/'>>uint;
自动面
=规则{“面”}
=“f”>>重复(3)[face3];
//5-忽略以“#”开头的每条记录
//6-忽略所有空行
自动跳过=空白|'#'>>*(字符-下线)>>(下线|下线);
自动推送=[](自动成员){
返回[成员](自动和ctx){
自动和数据=获取(ctx);
auto&v=std::invoke(成员,data.elements.back());
v、 向后推(std::move(_attr(ctx));
};
};
自动行_of=[](自动p,自动成员){
返回*(p[推送(成员)]>>(eoi |+eol));
};
自动对象=*跳过(跳过器)[
*下线>>&位置[新元素]>>
(位置和元素::位置)的行数>>
(tex和元素::texcoords)的行数>>
线(法线和元素::法线)>>
线(面和元素::面)
];
}
#如果有
#包括
#包括
无效转储(元素常量和el){
自动和[pos、tex、nrm、fac]=el;
fmt::打印(“位置:{}\n”
“texcoords:{}\n”
“法线:{}\n”
“面:{}\n\n”、pos、tex、nrm、fac);
}
#否则
无效转储(元素常量&){}
#恩迪夫
#包括
#包括
int main(){
std::ifstream ifs(“input.txt”);
boost::spirit::istream_迭代器f(ifs>>std::noskipws),l;
OBJ数据;
if(x3::parse(f,l,x3::with(data)[Parser::OBJ])){

std::难道有很多灵魂的答案(有些是我的)在这个网站上展示了这一点。我将在今天晚上的某个时候重温一个更适合你的描述的现代版本。非常感谢你,介绍了很多想法和选择,只是一个很好的工作!我真的鼓励你考虑在你的空闲时间写一个ObjLoad,因为有更多的选项和VAR。文件中的iants(可以提供更多信息)。我遇到了一些这样做的库,但只使用标准库,这很好,但效率较低。对于任何图形应用程序,通常都有很多OBJ文件,每个文件都可能非常大,甚至有数百万条记录!再次感谢,干杯:-)很有趣,一个没有任何语义操作的版本,并且带有iostream output需要一个在X3开发中的补丁,不过(我把1.74头放在那里)/HT@NikitaKniazev
Yay, 2 elements
positions: {(-0.01705, -0.017928, 0.005579), (-0.014504, -0.017928, 0.010577), (-0.0, -0.017928, 0.017967)}
texcoords: {(0.397581, 0.004762), (0.397544, 0.034263), (0.397507, 0.063764)}
normals: {(-0.951057, 0.0, 0.309016), (-0.809017, 0.0, 0.587785), (-0.587785, 0.0, 0.809017)}
faces: {{1, 1, 1, 2, 2, 2, 21, 4, 3}, {21, 4, 3, 2, 2, 2, 22, 3, 4}, {3, 5, 5, 4, 7, 7, 23, 6, 6}}

positions: {(-0.014504, 0.017928, -0.010499), (-0.01705, 0.017928, -0.005501), (-0.0, 0.017928, 3.9e-05)}
texcoords: {(0.063001, 0.262615), (0.073837, 0.268136), (0.089861, 0.299584)}
normals: {(0.0, 1.0, -2e-06), (0.0, 1.0, -2e-06), (0.0, 1.0, -0.0)}
faces: {{36, 80, 78, 37, 81, 79, 42, 66, 64}, {37, 81, 79, 38, 82, 80, 42, 66, 64}, {40, 84, 82, 21, 64, 62, 42, 66, 64}}
//#define HAVE_FMT
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/adapted/std_array.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <fstream>

using Position = std::tuple<double, double, double>;
using Coords   = std::tuple<double, double>;
using Normal   = std::tuple<double, double, double>;
//using Face3    = std::array<uint32_t, 3>;
//using Face9    = std::array<Face3, 3>; // 9
using Face9    = std::vector<uint32_t>; // hmmm

struct Element {
    std::vector<Position> positions;
    std::vector<Coords>   texcoords;
    std::vector<Normal>   normals;
    std::vector<Face9>    faces;
};

struct OBJ {
    std::vector<Element> elements;
};

namespace x3 = boost::spirit::x3;

namespace Parser {
    using namespace x3;

    auto new_element = [](auto& ctx) {
        auto& data = get<OBJ>(ctx);
        data.elements.emplace_back();
    };

    // 1- store each record begins with 'v' into 'positions'
    auto pos
        = rule<struct _pos, Position> {"pos"}
        = "v" >> double_ >> double_ >> double_;

    // 2- store each record begins with 'vt' into 'texcoords'
    auto tex
        = rule<struct _tex, Coords> {"tex"}
        = "vt" >> double_ >> double_;

    // 3- store each record begins with 'vn' into 'normals'
    auto normals
        = rule<struct _norm, Normal> {"normal"}
        = "vn" >> double_ >> double_ >> double_;

    // 4- store each record begins with 'f' into 'faces'.
    //    The integers are separated either with '/' or a blank-space (Each
    //    record with three groups, each group with three integers)

    auto face3
        = rule<struct _face3, Face9> {"face3"}
        = uint_ >> '/' >> uint_ >> '/' >> uint_;
    auto faces
        = rule<struct _faces, Face9> {"faces"}
        = "f" >> repeat(3) [ face3 ];

    // 5- ignore each record begins with '#'
    // 6- ignoring all empty lines
    auto skipper = blank | '#' >> *(char_ - eol) >> (eol|eoi);

    auto push = [](auto member) {
        return [member](auto& ctx) {
            auto& data = get<OBJ>(ctx);
            auto& v = std::invoke(member, data.elements.back());
            v.push_back(std::move(_attr(ctx)));
        };
    };

    auto lines_of = [](auto p, auto member) {
        return *(p [ push(member) ] >> (eoi|+eol));
    };

    auto OBJ = *skip(skipper) [
        *eol >> &pos [ new_element ] >>
        lines_of(pos,     &Element::positions) >>
        lines_of(tex,     &Element::texcoords) >>
        lines_of(normals, &Element::normals) >>
        lines_of(faces,   &Element::faces)
    ];
}

#ifdef HAVE_FMT
#include <fmt/printf.h>
#include <fmt/ranges.h>
void dump(Element const& el) {
    auto& [pos,tex,nrm,fac] = el;
    fmt::print("positions: {}\n"
            "texcoords: {}\n"
            "normals: {}\n"
            "faces: {}\n\n", pos, tex, nrm, fac);
}
#else
void dump(Element const&) { }
#endif

#include <iostream>
#include <iomanip>
int main() {
    std::ifstream ifs("input.txt");
    boost::spirit::istream_iterator f(ifs >> std::noskipws), l;

    OBJ data;
    if (x3::parse(f, l, x3::with<OBJ>(data) [ Parser::OBJ ])) {
        std::cout << "Yay, " << data.elements.size() << " elements\n";

        for (auto& el : data.elements) {
            dump(el);
        }
    } else {
        std::cout << "Failed\n";
    }

    if (f!=l) {
        std::cout << "Remaining unparsed: " << std::quoted(std::string(f,l)) << "\n";
    }
}