获取找到给定选项的 INI 文件的行号的跨平台方法

时间:2023-02-12
本文介绍了获取找到给定选项的 INI 文件的行号的跨平台方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

寻找一些能够返回 INI 文件的行号的 C++ 库(如 boost::program_options),其中找到了给定的选项或部分.

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.

用例:

  1. 我要求该库在[SSS]"部分中查找值vvv".库返回在[SSS]"部分中找到vvv"的行号,或 -1.它使我能够说第 55 行:vvv 必须小于 256".

  1. 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 文件并验证它们的名称.当发现一些疯狂的部分时,我告诉:第 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 比猛犸象更古老",但目前我必须将大型 Windows 项目移植到跨平台,并且无法很快摆脱 .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.

推荐答案

再次借机玩 Boost Spirit.这次我开始玩line_pos_iterator.

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

  • POSITIONINFO == 0
    • 输入正在流式传输
    • 输出是原始字符串(好吧,map > 用于部分)
    • 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;
      };
      

      这意味着结果 map> 能够准确报告标记单个文本节点的 (line,col) 起点和终点.查看演示的测试输出

      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

      注释(#/* ... */ 样式)已实现

      Comments (#, /* ... */ style) have been implemented

      容忍"空格

      name = value # 使用注释强制包含尾随空格替代 = 转义带斜线

      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. :)

      所有代码、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 '
      ' : os << "\r"; break;
                      case '
      ' : os << "\n"; break;
                      case '	' : os << "\t"; break;
                      case '' : 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_(" 	") | 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 文件的行号的跨平台方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一篇:如何检查 C++ std::string 是否以某个字符串开头,并将子字符串转换为 int? 下一篇:在 C++ 中解析数学表达式

相关文章

最新文章