使用 boost::p rogram_options 来解析文本文件是个好主意吗?
It's a good idea to use boost::program_options to parse a text file?
我必须处理许多语法和语义定义良好的文件,例如:
- 第一行是一个带有特殊信息的标题
- 其他行在行的开头包含一个键值,告诉您如何解析和处理该行的内容
- 如果有注释,则以给定的标记开头
- 等等
现在,据我所知,boost::program_options
做了几乎相同的工作,但我只关心导入这些文本文件的内容,而不需要任何额外的工作,只需解析它并将其存储在我的数据结构中。
对我来说,关键的一步是我希望能够用进行解析
- 正则表达式,因为我需要检测不同的语义,而且我真的无法想象其他方法来做到这一点
- 错误检查(损坏的文件,即使在解析整个文件后也不匹配的密钥,等等…)
那么,我可以用这个图书馆做这份工作吗?还有更实用的方法吗?
好的,Spirit语法的起点
_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文件子集语法。
注意:这是一个相当简单的语法。但是,你知道,第一件事是第一位的。实际上,我可能会考虑使用spirit存储库中的关键字列表解析器。它具有"要求"不同"字段类型"出现一定次数的功能。
注意:灵因果报应(以及大约50行其他代码)仅用于演示目的。
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 {"
"ntNs:" << (auto_ | "(unspecified)")
<< "ntKa:" << (stream | "(unspecified)")
<< "ntKd:" << (stream | "(unspecified)")
<< "ntKs:" << (stream | "(unspecified)")
<< "ntd:" << (stream | "(unspecified)")
<< "ntillum:" << (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");
}
是的,至少如果您配置的文件像键值对映射一样简单(比如simple.ini)
来自文件:
program_options库允许程序开发人员获得程序选项,即来自用户的(名称、值)对,通过常规方法,如命令行和配置文件。
选项可以从任何地方读取。迟早命令行对您的用户来说还不够,您需要配置文件或甚至可能环境变量。可以添加这些您付出了巨大的努力。
有关详细信息,请参阅"多源"示例。
但是,如果您需要(或者将来可能需要)更复杂的配置文件(例如XML、JSON或二进制),那么使用独立库是值得的。
这很有可能,但不一定方便。如果您想解析任何您想使用的解析器——是使用现有的解析器还是自己编写解析器取决于您正在解析的内容。
如果无法使用任何现有工具解析您的格式,那么只需编写自己的解析器即可。您可以将lex/flex/flex++与yacc/bison/bison++或boost::spirit一起使用。
我认为,从长远来看,学习维护自己的解析器将比强制调整boost::program_options配置更有用,但不如使用已经符合您需求的现有解析器更方便。
- 文本文件中的单词链表
- 在C++程序中输入的文本文件将不起作用,除非文本被复制和粘贴
- 如何将内容数组写入文本文件?
- 无法通过空白将文本文件行分隔为矢量
- 我正在使用嵌套的while循环来解析具有多行的文本文件,但由于某种原因,它只通过第一行,我不知道为什么
- C++将文本文件中的数据读取到结构数组中
- 在指针的帮助下,文本文件中单词的频率
- 将字符指针十六进制转换为字符串并保存在文本文件C++中
- 将值从二维数组输出到文本文件
- 如何使用 c++ 实现并发文件/文本编辑?
- 我需要转换一些代码,以便它适用于输入和输出文件文本
- 通过套接字发送二进制文件.文本文件有效,其他文件无效
- 将大型数据文件拆分为多个小文件(文本格式)
- 如何读取输入文件(文本文件)并将输入验证为有效整数
- 从文本文件/文本流中读取Q字符串错误
- 从文本文件(文本解析器)C++中读取行和列的逻辑
- 如何将*.css文件(文本文件)的内容与附加信息存储在新文件中?
- 关于内存映射文件和使用大文件文本编辑器
- 如何在读取文件文本时跳过"n"
- 使用libzip从.zip获取文件(文本除外)