使用boost :: program_options解析文本文件是一个好主意吗? [英] It's a good idea to use boost::program_options to parse a text file?

查看:58
本文介绍了使用boost :: program_options解析文本文件是一个好主意吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须处理许多具有明确定义的语法和语义的文件,例如:

I have to deal with a lot of files with a well defined syntax and semantic, for example:

  • 第一行是带有特殊信息的标题
  • 其他各行的开头都包含一个键值,告诉您如何解析和处理该行的内容
  • 如果有评论,则以给定标记开头
  • 等等等...

现在boost::program_options几乎可以完成相同的工作,但是我只关心导入这些文本文件的内容,而无需中间进行任何额外的工作,只需对其进行解析并将其存储在我的计算机中即可.数据结构.

now boost::program_options, as far as I can tell, does pretty much the same job, but I only care about importing the content of those text file, without any extra work in between, just parse it and store it in my data structure .

对我来说关键的一步是我希望能够使用以下方法进行解析:

the key step for me is that I would like to be able to do this parsing with:

  • 正则表达式,因为我需要检测不同的语义,而且我真的无法想象实现此目的的另一种方式
  • 错误检查(损坏的文件,甚至在分析整个文件后都没有匹配的密钥等)

那么,我可以使用这个库来完成这项工作吗?还有一种更实用的方法吗?

so, I can use this library for this job ? There is a more functional approach ?

推荐答案

好,是Spirit语法的起点

Okay, a starting point for a Spirit grammar

_Name    = "newmtl" >> lexeme [ +graph ];
_Ns      = "Ns"     >> double_;
_Ka      = "Ka"     >> double_ >> double_ >> double_;
_Kd      = "Kd"     >> double_ >> double_ >> double_;
_Ks      = "Ks"     >> double_ >> double_ >> double_;
_d       = "d"      >> double_;
_illum  %= "illum"  >> qi::int_ [ _pass = (_1>=0) && (_1<=10) ];

comment  = '#' >> *(char_ - eol);

statement=
         comment
       | _Ns    [ bind(&material::_Ns, _r1) = _1 ]
       | _Ka    [ bind(&material::_Ka, _r1) = _1 ]
       | _Kd    [ bind(&material::_Kd, _r1) = _1 ]
       | _Ks    [ bind(&material::_Ks, _r1) = _1 ]
       | _d     [ bind(&material::_d,  _r1) = _1 ]
       | _illum [ bind(&material::_illum, _r1) = _1 ]
       ;

_material = -comment % eol
        >> _Name [ bind(&material::_Name, _val) = _1 ] >> eol
        >> -statement(_val) % eol;

start = _material % -eol;

我仅从您的示例文件中实现了MTL文件子集语法.

I only implemented the MTL file subset grammar from your sample files.

注意:这是一种简单的语法.但是,首先,首先要知道.实际上,我可能会考虑使用

Note: This is rather a simplistic grammar. But, you know, first things first. In reality I'd probably consider using the keyword list parser from the spirit repository. It has facilities to 'require' certain number of occurrences for the different 'field types'.

注意:Spirit Karma(以及大约50余行其他代码)仅在此处用于演示目的.

Note: Spirit Karma (and some ~50 other lines of code) are only here for demonstrational purposes.

具有untitled.mtl

# Blender MTL File: 'None'
# Material Count: 2

newmtl None
Ns 0
Ka 0.000000 0.000000 0.000000
Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8
d 1
illum 2
# Added just for testing:

newmtl Demo
Ns 1
Ks 0.9 0.9 0.9
d 42
illum 7

输出内容为

phrase_parse -> true
remaining input: ''
void dump(const T&) [with T = std::vector<blender::mtl::material>]
-----
material {
    Ns:0
    Ka:{r:0,g:0,b:0}
    Kd:{r:0.8,g:0.8,b:0.8}
    Ks:{r:0.8,g:0.8,b:0.8}
    d:1
    illum:2(Highlight on)
}
material {
    Ns:1
    Ka:(unspecified)
    Kd:(unspecified)
    Ks:{r:0.9,g:0.9,b:0.9}
    d:42
    illum:7(Transparency: Refraction on/Reflection: Fresnel on and Ray trace on)
}
-----

这是列表

#define BOOST_SPIRIT_USE_PHOENIX_V3
#define BOOST_SPIRIT_DEBUG

#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp> // for debug output/streaming
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>

namespace qi = boost::spirit::qi;
namespace phx= boost::phoenix;

namespace wavefront { namespace obj
{
} }

namespace blender { namespace mtl // material?
{
    struct Ns { int exponent; }; // specular exponent
    struct Reflectivity { double r, g, b; };

    using Name = std::string;
    using Ka   = Reflectivity;
    using Kd   = Reflectivity;
    using Ks   = Reflectivity;

    using dissolve_factor = double;
    enum class illumination_model {
            color,          // 0     Color on and Ambient off
            color_ambient,  // 1     Color on and Ambient on
            highlight,      // 2     Highlight on
            reflection_ray, // 3     Reflection on and Ray trace on
            glass_ray,      // 4     Transparency: Glass on
                            //       Reflection: Ray trace on
            fresnel_ray,    // 5     Reflection: Fresnel on and Ray trace on
            refract_ray,    // 6     Transparency: Refraction on
                            //       Reflection: Fresnel off and Ray trace on
            refract_ray_fresnel,// 7 Transparency: Refraction on
                            //       Reflection: Fresnel on and Ray trace on
            reflection,     // 8     Reflection on and Ray trace off
            glass,          // 9     Transparency: Glass on
                            //       Reflection: Ray trace off
            shadow_invis,   // 10    Casts shadows onto invisible surfaces
    };

    struct material
    {
        Name                                _Name;
        boost::optional<Ns>                 _Ns;
        boost::optional<Reflectivity>       _Ka;
        boost::optional<Reflectivity>       _Kd;
        boost::optional<Reflectivity>       _Ks;
        boost::optional<dissolve_factor>    _d;
        boost::optional<illumination_model> _illum;
    };

    using mtl_file = std::vector<material>;

    ///////////////////////////////////////////////////////////////////////
    // Debug output helpers
    std::ostream& operator<<(std::ostream& os, blender::mtl::illumination_model o)
    {
        using blender::mtl::illumination_model;
        switch(o)
        {
            case illumination_model::color:               return os << "0(Color on and Ambient off)";
            case illumination_model::color_ambient:       return os << "1(Color on and Ambient on)";
            case illumination_model::highlight:           return os << "2(Highlight on)";
            case illumination_model::reflection_ray:      return os << "3(Reflection on and Ray trace on)";
            case illumination_model::glass_ray:           return os << "4(Transparency: Glass on/Reflection: Ray trace on)";
            case illumination_model::fresnel_ray:         return os << "5(Reflection: Fresnel on and Ray trace on)";
            case illumination_model::refract_ray:         return os << "6(Transparency: Refraction on/Reflection: Fresnel off and Ray trace on)";
            case illumination_model::refract_ray_fresnel: return os << "7(Transparency: Refraction on/Reflection: Fresnel on and Ray trace on)";
            case illumination_model::reflection:          return os << "8(Reflection on and Ray trace off)";
            case illumination_model::glass:               return os << "9(Transparency: Glass on/Reflection: Ray trace off)";
            case illumination_model::shadow_invis:        return os << "10(Casts shadows onto invisible surfaces)";
            default: return os << "ILLEGAL VALUE";
        }
    }

    std::ostream& operator<<(std::ostream& os, blender::mtl::Reflectivity const& o)
    {
        return os << "{r:" << o.r << ",g:" << o.g << ",b:" << o.b << "}";
    }

    std::ostream& operator<<(std::ostream& os, blender::mtl::material const& o)
    {
        using namespace boost::spirit::karma;
        return os << format("material {"
                "\n\tNs:"    << (auto_  | "(unspecified)")
                << "\n\tKa:"    << (stream | "(unspecified)")
                << "\n\tKd:"    << (stream | "(unspecified)")
                << "\n\tKs:"    << (stream | "(unspecified)")
                << "\n\td:"     << (stream | "(unspecified)")
                << "\n\tillum:" << (stream | "(unspecified)")
                << "\n}", o);
    }
} }

BOOST_FUSION_ADAPT_STRUCT(blender::mtl::Reflectivity,(double, r)(double, g)(double, b))
BOOST_FUSION_ADAPT_STRUCT(blender::mtl::Ns, (int, exponent))
BOOST_FUSION_ADAPT_STRUCT(blender::mtl::material,
        (boost::optional<blender::mtl::Ns>, _Ns)
        (boost::optional<blender::mtl::Ka>, _Ka)
        (boost::optional<blender::mtl::Kd>, _Kd)
        (boost::optional<blender::mtl::Ks>, _Ks)
        (boost::optional<blender::mtl::dissolve_factor>, _d)
        (boost::optional<blender::mtl::illumination_model>, _illum))

namespace blender { namespace mtl { namespace parsing
{
    template <typename It>
        struct grammar : qi::grammar<It, qi::blank_type, mtl_file()>
    {
        template <typename T=qi::unused_type> using rule = qi::rule<It, qi::blank_type, T>;

        rule<Name()>               _Name;
        rule<Ns()>                 _Ns;
        rule<Reflectivity()>       _Ka;
        rule<Reflectivity()>       _Kd;
        rule<Reflectivity()>       _Ks;
        rule<dissolve_factor()>    _d;
        rule<illumination_model()> _illum;

        rule<mtl_file()> start;
        rule<material()> _material;
        rule<void(material&)> statement;
        rule<> comment;

        grammar() : grammar::base_type(start)
        {
            using namespace qi;
            using phx::bind;
            using blender::mtl::material;

            _Name    = "newmtl" >> lexeme [ +graph ];
            _Ns      = "Ns"     >> double_;
            _Ka      = "Ka"     >> double_ >> double_ >> double_;
            _Kd      = "Kd"     >> double_ >> double_ >> double_;
            _Ks      = "Ks"     >> double_ >> double_ >> double_;
            _d       = "d"      >> double_;
            _illum  %= "illum"  >> qi::int_ [ _pass = (_1>=0) && (_1<=10) ];

            comment  = '#' >> *(char_ - eol);

            statement=
                    comment
                | _Ns    [ bind(&material::_Ns, _r1) = _1 ]
                | _Ka    [ bind(&material::_Ka, _r1) = _1 ]
                | _Kd    [ bind(&material::_Kd, _r1) = _1 ]
                | _Ks    [ bind(&material::_Ks, _r1) = _1 ]
                | _d     [ bind(&material::_d,  _r1) = _1 ]
                | _illum [ bind(&material::_illum, _r1) = _1 ]
                ;

            _material = -comment % eol
                    >> _Name [ bind(&material::_Name, _val) = _1 ] >> eol
                    >> -statement(_val) % eol;

            start = _material % -eol;

            BOOST_SPIRIT_DEBUG_NODES(
                    (start)
                    (statement)
                    (_material)
                    (_Name) (_Ns) (_Ka) (_Kd) (_Ks) (_d) (_illum)
                    (comment))
        }

};

} } }

#include <fstream>

template <typename T>
void dump(T const& data)
{
    using namespace boost::spirit::karma;
    std::cout << __PRETTY_FUNCTION__
        << "\n-----\n"
        << format(stream % eol, data)
        << "\n-----\n";
}

void testMtl(const char* const fname)
{
    std::ifstream mtl(fname, std::ios::binary);
    mtl.unsetf(std::ios::skipws);
    boost::spirit::istream_iterator f(mtl), l;

    using namespace blender::mtl::parsing;
    static const grammar<decltype(f)> p;

    blender::mtl::mtl_file data;
    bool ok = qi::phrase_parse(f, l, p, qi::blank, data);

    std::cout << "phrase_parse -> " << std::boolalpha << ok << "\n";
    std::cout << "remaining input: '" << std::string(f,l) << "'\n";

    dump(data);
}

int main()
{
    testMtl("untitled.mtl");
}

这篇关于使用boost :: program_options解析文本文件是一个好主意吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆