跨平台的方式来获得,其中给定的选项被发现INI文件的行号 [英] Cross-platform way to get line number of an INI file where given option was found
问题描述
寻找一些C ++库(如升压:: program_options),它能够返回一个INI文件,其中给定的选项或部分被发现的行号。
Looking for some C++ library (like boost::program_options) that is able to return line number of an INI file, where the given option or section was found.
使用情况:
-
我问这个图书馆找到一节值VVV[SSS]。图书馆返回行号VVV一节[SSS]被发现,或-1。它给我说55行:VVV必须< 256的能力。
I ask that library to find value "vvv" in a section "[SSS]". Library returns line number where "vvv" in section "[SSS]" is found, or -1. It gives me an ability to say "line 55: vvv must be < 256".
我对重复部分INI文件并验证他们的名字。当一些野生secsion被发现,我告诉:行55:节[哈哈哈]是未知的。
I iterate INI file for sections and validate their names. When some wild secsion is found, i tell: "line 55: section [Hahaha] is unknown".
更新:我知道INI比猛犸前辈,但目前我有到港大窗户工程跨平台,并且不能得到尽快摆脱.ini文件
update: i know about "INI is older than mammoth", but currently i have to port large windows project to cross-platform and cannot get rid of .ini files soon.
推荐答案
再一次,趁机用升压精神的发挥。这一次,我玩<一个href=\"http://www.boost.org/doc/libs/1_48_0/libs/spirit/doc/html/spirit/support/line_pos_iterator.html\"><$c$c>line_pos_iterator$c$c>.
Once again, took the opportunity to play with Boost Spirit. This time I got to play with line_pos_iterator
.
下面是我的劳动成果: https://gist.github.com/1425972
Here is the fruit of my labour: https://gist.github.com/1425972
- 当
POSITIONINFO == 0
- 输入流
- 输出为原始字符串(当然,
地图&LT;字符串,地图&LT;字符串,字符串&GT;&GT;
的部分)
- When
POSITIONINFO == 0
- input is streaming
- output is raw strings (well,
map<string, map<string, string> >
for the sections)
在
POSITIONINFO == 1
- 输入缓冲
-
输出
textnode_t
:
struct textnode_t { int sline, eline, scol, ecol; string_t text; };
这意味着生成的
地图&LT; textnode_t,地图&LT; textnode_t,textnode_t&GT; &GT;
是能够报告什么(行,列)开始和结束点关口个别文本节点。 查看演示测试输出的This means that the resulting
map<textnode_t, map<textnode_t, textnode_t> >
is able to report exactly what (line,col) start and end points mark the individual text nodes. See test output for a demo评论(
#
,/ * ... * /
风格)已实施空白的'容忍'
名称=值#使用注释强制纳入尾随空白
替代=逃避\\与斜线\\name = value # use a comment to force inclusion of trailing whitespace alternative = escape\ with slash\
去逃避
斜线
留作练习注意 C ++ 11的支持是不会必需的,但我用它来转储解析的结果。我懒得把它与C ++ 03冗长的迭代式的写作。 :)
NOTE C++11 support is NOT required, but I used it to dump the result of the parse. I'm too lazy to write it with C++03 verbose iterator style. :)
所有code,makefile文件,example.ini可以在这里找到: https://gist.github.com/1425972一>
All code, makefile, example.ini can be found here: https://gist.github.com/1425972
/* inireader.h */ #pragma once #define POSITIONINFO 0 #include <map> #include <string> #include <iterator> #include <boost/tuple/tuple_comparison.hpp> template <typename S=std::string, typename Cmp=std::less<S> > class IniFile { public: IniFile(Cmp cmp=Cmp()) : _cmp(cmp) {} IniFile(const std::string& filename, Cmp cmp=Cmp()) : _cmp(cmp) { open(filename); } void open(const std::string& filename); typedef S string_t; #if POSITIONINFO struct textnode_t { int sline, eline, scol, ecol; string_t text; operator const string_t&() const { return text; } friend std::ostream& operator<<(std::ostream& os, const textnode_t& t) { os << "[L:" << t.sline << ",C" << t.scol << " .. L" << t.eline << ",C" << t.ecol << ":"; for (typename string_t::const_iterator it=t.text.begin(); it!=t.text.end(); ++it) switch (*it) { case '\r' : os << "\\r"; break; case '\n' : os << "\\n"; break; case '\t' : os << "\\t"; break; case '\0' : os << "\\0"; break; default: os << *it ; break; } return os << "]"; } bool operator<(const textnode_t& o) const { return boost::tie(text/*, sline, eline, scol, ecol*/) < boost::tie(o.text/*, o.sline, o.eline, o.scol, o.ecol*/); } textnode_t() : sline(0), eline(0), scol(0), ecol(0) { } }; #else typedef string_t textnode_t; #endif typedef std::pair<textnode_t, textnode_t> keyvalue_t; typedef std::map<textnode_t, textnode_t> section_t; typedef std::map<textnode_t, section_t> sections_t; private: Cmp _cmp; }; /////////////////////////////////////// // template implementation //#define BOOST_SPIRIT_DEBUG #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/support_istream_iterator.hpp> #include <boost/spirit/include/support_line_pos_iterator.hpp> #include <boost/spirit/include/phoenix.hpp> #include <boost/fusion/adapted/std_pair.hpp> #include <fstream> namespace qi = boost::spirit::qi; namespace phx= boost::phoenix; namespace inireader { struct printer { printer(std::ostream& os) : _os(os) {} std::ostream& _os; typedef boost::spirit::utf8_string string; void element(string const& tag, string const& value, int depth) const { for (int i = 0; i < (depth*4); ++i) // indent to depth _os << ' '; _os << "tag: " << tag; if (value != "") _os << ", value: " << value; _os << std::endl; } }; void print_info(std::ostream& os, boost::spirit::info const& what) { using boost::spirit::basic_info_walker; printer pr(os); basic_info_walker<printer> walker(pr, what.tag, 0); boost::apply_visitor(walker, what.value); } template <typename It, typename Skipper, typename Ini> struct Grammar : qi::grammar<It, typename Ini::sections_t(), Skipper> { typedef typename Ini::string_t string_t; typedef typename Ini::textnode_t textnode_t; struct textbuilder { template <typename> struct result { typedef textnode_t type; }; textbuilder(It begin) : _begin(begin) { } textnode_t operator()(const boost::iterator_range<It>& iters) const { #if !POSITIONINFO return textnode_t(std::begin(iters), std::end(iters)); #else using boost::spirit::get_line; using boost::spirit::get_line_start; using boost::spirit::get_column; textnode_t element; element.text = string_t (std::begin(iters) , std::end(iters)); element.sline = get_line (std::begin(iters)); element.eline = get_line (std::end(iters)); It sol = get_line_start (_begin , std::begin(iters)); element.scol = get_column (sol , std::begin(iters)); element.ecol = get_column (sol , std::end(iters)); return element; #endif } private: const It _begin; } makenode; Grammar(It begin) : Grammar::base_type(inifile), makenode(begin) { using namespace qi; txt_ch = (lit('\\') > char_) | (char_ - (eol | '#' | "/*")); key = raw [ lexeme [ +(txt_ch - char_("=")) ] ] [ _val = phx::bind(makenode, _1) ]; value = raw [ lexeme [ +txt_ch ] ] [ _val = phx::bind(makenode, _1) ]; pair %= key > '=' > value; heading = ('[' > raw [ +~char_(']') ] > ']') [ _val = phx::bind(makenode, _1) ]; section %= heading >> +eol >> -((pair-heading) % +eol); inifile %= -(section % +eol) >> *eol > eoi; comment = ('#' >> *(char_ - eol)) | ("/*" > *(char_ - "*/") > "*/"); //BOOST_SPIRIT_DEBUG_NODE(comment); //BOOST_SPIRIT_DEBUG_NODE(txt_ch); BOOST_SPIRIT_DEBUG_NODE(heading); BOOST_SPIRIT_DEBUG_NODE(section); BOOST_SPIRIT_DEBUG_NODE(key); BOOST_SPIRIT_DEBUG_NODE(value); BOOST_SPIRIT_DEBUG_NODE(pair); BOOST_SPIRIT_DEBUG_NODE(inifile); } typedef typename Ini::keyvalue_t keyvalue_t; typedef typename Ini::section_t section_t; typedef typename Ini::sections_t sections_t; typedef typename string_t::value_type Char; qi::rule<It> comment; qi::rule<It, Char()> txt_ch; qi::rule<It, textnode_t(), Skipper> key, value, heading; qi::rule<It, keyvalue_t(), Skipper> pair; qi::rule<It, std::pair<textnode_t, section_t>(), Skipper> section; qi::rule<It, sections_t(), Skipper> inifile; }; template <typename It, typename Builder> typename Builder::template result<void>::type fragment(const It& first, const It& last, const Builder& builder) { size_t len = std::distance(first, last); It frag_end = first; std::advance(frag_end, std::min(10ul, len)); return builder(boost::iterator_range<It>(first, frag_end)); } } template <typename S, typename Cmp> void IniFile<S, Cmp>::open(const std::string& filename) { using namespace qi; std::ifstream ifs(filename.c_str()); ifs.unsetf(std::ios::skipws); #if POSITIONINFO typedef std::string::const_iterator RawIt; typedef boost::spirit::line_pos_iterator<RawIt> It; typedef rule<It> Skipper; std::string buffer(std::istreambuf_iterator<char>(ifs), (std::istreambuf_iterator<char>())); It f(buffer.begin()), l(buffer.end()); #else typedef boost::spirit::istream_iterator It; typedef rule<It> Skipper; It f(ifs), l; #endif inireader::Grammar<It, Skipper, IniFile<S, Cmp> > grammar(f); Skipper skip = char_(" \t") | grammar.comment; try { sections_t data; bool ok = phrase_parse(f, l, grammar, skip, data); if (ok) { std::cout << "Parse success!" << std::endl; ///////// C++11 specific features for quick display ////////// for (auto& section : data) { std::cout << "[" << section.first << "]" << std::endl; for (auto& pair : section.second) std::cout << pair.first << " = " << pair.second << std::endl; ///////// End C++11 specific ///////////////////////////////// } } else { std::cerr << "Parse failed" << std::endl; } } catch (const qi::expectation_failure<It>& e) { std::cerr << "Exception: " << e.what() << " " << inireader::fragment(e.first, e.last, grammar.makenode) << "... "; inireader::print_info(std::cerr, e.what_); } if (f!=l) { std::cerr << "Stopped at: '" << inireader::fragment(f, l, grammar.makenode) << "'" << std::endl; } }
演示输入
[Cat1] name1=100 #skipped name2=200 \#not \\skipped name3= dhfj dhjgfd/* skipped */ [Cat_2] UsagePage=9 Usage=19 Offset=0x1204 /* [Cat_2_bak] UsagePage=9 Usage=19 Offset=0x1204 */ [Cat_3] UsagePage=12 Usage=39 #Usage4=39 Offset=0x12304
演示输出(POSITIONINFO == 0)
Parse success! [Cat1] name1 = 100 name2 = 200 \#not \\skipped name3 = dhfj dhjgfd [Cat_2] Offset = 0x1204 Usage = 19 UsagePage = 9 [Cat_3] Offset = 0x12304 Usage = 39 UsagePage = 12
演示输出(POSITIONINFO == 1)
Parse success! [[L:1,C2 .. L1,C6:Cat1]] [L:2,C2 .. L2,C7:name1] = [L:2,C8 .. L2,C12:100 ] [L:6,C2 .. L6,C7:name2] = [L:6,C8 .. L6,C27:200 \#not \\skipped] [L:7,C2 .. L7,C7:name3] = [L:7,C11 .. L7,C22:dhfj dhjgfd] [[L:13,C3 .. L13,C8:Cat_2]] [L:16,C2 .. L16,C8:Offset] = [L:16,C9 .. L16,C15:0x1204] [L:15,C2 .. L15,C7:Usage] = [L:15,C8 .. L15,C10:19] [L:14,C2 .. L14,C11:UsagePage] = [L:14,C12 .. L14,C13:9] [[L:25,C3 .. L25,C8:Cat_3]] [L:29,C2 .. L29,C8:Offset] = [L:29,C9 .. L29,C16:0x12304] [L:27,C2 .. L27,C7:Usage] = [L:27,C8 .. L27,C10:39] [L:26,C2 .. L26,C11:UsagePage] = [L:26,C12 .. L26,C14:12]
这篇关于跨平台的方式来获得,其中给定的选项被发现INI文件的行号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!