zoukankan      html  css  js  c++  java
  • Properties --- C++读配置信息的类

    http://blog.csdn.net/billow_zhang/article/details/4304980

    在开发实践中,积累了一些通用的C++ 类库,在此写出来给大家分享。也希望能给出更好的建议。工具库的名字是xtl——Properties 类。分两部分介绍。这篇介绍类的定义。下一篇将介绍类的实现。 。这篇介绍其中的读配置文件的类

     下面是类的定义的头文件:

      1 
      2 /*xtl/Properties.h
      3   Author: ZhangTao
      4   Date: Nov 6, 2008
      5 */
      6 
      7 # ifndef Properties_h
      8 # define Properties_h
      9 
    10 # include       <algorithm>
    11 # include       <iterator>
    12 
    13 # include       <iostream>
    14 # include       <string>
    15 # include       <map>
    16 
    17 namespace std  {
    18   // <map> member output operator
    19   template<typename _U, typename _V>
    20     ostream& operator<< (ostream& os, const pair<_U, _V>& val)  {
    21     return os << val.first << " = " << val.second;
    22   }
    23 
    24   // <map> output operator, ie. all members in <map> output to <ostream>
    25   template<typename _U, typename _V>
    26     ostream& operator<< (ostream& os, const map<_U, _V>& val)  {
    27     copy(val.begin(), val.end(), ostream_iterator< pair<_U, _V> >(os, "/n"));
    28     return os;
    29   }
    30 }  // end of <namespace std>
    31 
    32 namespace xtl   {
    33 
    34 class Properties : public std::map<std::string, std::string> {
    35   public:

    36     Properties() {};
    37     Properties(const char* fname, const char* section = "")  {
    38       load(fname, section);
    39     }
    40     Properties(std::istream& is, const char* section = "")  {
    41       load(is, section);
    42     }
    43 
    44     void load(const char* fname, const char* section = "");
    45     void load(std::istream& is, const char* section = "");
    46     void loadXML(const char* fname, const char* section = "");
    47     void loadXML(std::istream& is, const char* section = "");
    48 
    49     const std::string& getProperty(const std::string& key) const;
    50 
    51     void list(std::ostream& os) const  {
    52       os << *this;
    53     }
    54 }; // end of <class Properties>
    55 
    56 const char* const XML_ENTRY = "entry";
    57 const char* const XML_KEY = "key";
    58 
    59 } // end of <namespace xtl>
    60 
    61 # endif /* end of <ifndef Properties_h> */
    62

    Properties 继承了stl 库的map 容器类。这样可以使用map 的各个接口实现。尤其是输出输入的重载的实现。

    18 到22 行定义了map 单个元素的输出重载;25 到29 行则定义了map 全部元素的输出重载。正是有了这些铺垫,才使得51 行的list 函数显得那么简练。实际上list 函数完全可以不需要,可以使用<< 重载输出。例如:

    Properties prop; 
    cout << prop; 
    等同于:
    prop.list(cout);

    Properties 的load 是用于从文件中读入数据的。它可以读入下面格式的文件:


    # props.conf 
    # for Properties class test

    TEST1=TEST1VALUE

    TEST2=TEST2VALUE 
    TEST3=TEST3VALUE 
    TEST4 = 100

    [Section0] 
    TEST01=TEST01VALUE 
    TEST02=TEST02VALUE 
    TEST03=TEST03VALUE 
    TEST04 = 200

    [Section1] 
    TEST11=TEST11VALUE 
    TEST12=TEST12VALUE 
    TEST13=TEST13VALUE 
    TEST14 = 300

    下面是测试程序:

    /* tst-properties
       test class Properties
    */

    # include       "xtl/Properties.h"

    int 
    main(int argc, char* argv[]) 

      const char* sec;

      if ( argc > 1 ) 
        sec = argv[1]; 
      else 
        sec = "";

      xtl::Properties prop(std::cin, sec); 
      prop.list(std::cout);

      if ( argc > 2 ) 
        std::cout << "Key:<" << argv[2] << "> Value:" << 
                    prop.getProperty(argv[2]) << "/n";

      return 0; 
    }

    上面的测试数据文件为 props.conf ,测试程序编译连接后的执行文件为 tst-properties 。 
    以下是运行结果。

    $ tst-properties <props.conf 
    TEST1 = TEST1VALUE
    TEST2 = TEST2VALUE
    TEST3 = TEST3VALUE
    TEST4 = 100

    $ tst-properties Section0 ITEM04 <props.conf 
    TEST01 = TEST01VALUE
    TEST02 = TEST02VALUE
    TEST03 = TEST03VALUE
    TEST04 = 200
    Key:<TEST04> Value:200

    由于继承了map类,相应的操作异常简练。最复杂的应该是load函数了。 
    load的实现,请阅下一篇文章。

    在第一部分中,列出了Properties的定义的头文件。这个文件中的load及loadXML接口参数是一样的。当初设计这个类的时候,主要是读ini格式的文件,后来又有了读XML格式文件的需求,才增加了loadXML的函数。这样以增加函数接口来扩展功能的方式显得比较丑陋,同时也说明,Properties的设计不能满足于读不同文件格式的需要。下面是针对这个问题,作出的重新的设计:

     1 
     2 /* xtl/Properties.h
     3   Author: ZhangTao
     4   Date: Nov 6, 2008
     5 */
     6 
     7 # ifndef Properties_h
     8 # define Properties_h
     9 
    10 # include       <algorithm>
    11 # include       <iterator>
    12 
    13 # include       <iostream>
    14 # include       <fstream>
    15 # include       <string>
    16 # include       <map>
    17 
    18 # include       "xtl/except.h"
    19 
    20 namespace std  {
    21   // <map> member output operator
    22   template<typename _U, typename _V>
    23   ostream& operator<< (ostream& os, const pair<_U, _V>& val)  {
    24     return os << val.first << " = " << val.second;
    25   }
    26 
    27   // <map> output operator, ie. all members in <map> output to <ostream>
    28   template<typename _U, typename _V>
    29   ostream& operator<< (ostream& os, const map<_U, _V>& val)  {
    30     copy(val.begin(), val.end(), ostream_iterator< pair<_U, _V> >(os, "/n"));
    31     return os;
    32   }
    33 }  // end of <namespace std>
    34 
    35 namespace xtl   {
    36 
    37 typedef std::map<std::string, std::string> PropMap;
    38 
    39 template<typename _Loader>
    40 class Properties : public PropMap  {
    41   public:
    42     Properties() {};
    43     Properties(const char* fname, const char* section = "")  {
    44       load(fname, section);
    45     }
    46     Properties(std::istream& is, const char* section = "")  {
    47       load(is, section);
    48     }
    49 
    50     void load(const char* fname, const char* section = "")  {
    51       std::ifstream ifs(fname);
    52 
    53       if ( !ifs )
    54         throw_fmtException("can not read <%s>", fname);
    55 
    56       load(ifs, section);
    57     }
    58 
    59     void load(std::istream& is, const char* section = "")  {
    60       loadFunc(*this, is, section);
    61     }
    62 
    63     const std::string& getProperty(const std::string& key) const  {
    64       static const std::string EmptyStr;
    65 
    66       const_iterator it = find(key);
    67 
    68       return it == end()? EmptyStr : it->second;
    69     }
    70 
    71     void list(std::ostream& os) const  { 
    72       os << *this;
    73     }
    74 
    75   private:
    76     _Loader loadFunc;   // load map data template function
    77 
    78 }; // end of <class Properties>
    79 
    80 
    81 } // end of <namespace xtl>
    82 
    83 # endif /* end of <ifndef Properties_h> */
    84

    修改的最大的变化是将Properties设计成类的模版。真正的实现需要提供_Load的模版类。读不同文件的方法就体现在_Load类的不同上。由于Properties成为了模版,其他原来在.cpp文件中的实现也放在头文件里定义了。类中最重要的功能就是根据一个名称取出相应的值。这正是选择map类的重要原因之一。map类中的find方法实现了这个基本功能。根据Properties 的实用习惯,在63到69行使用getProperty对find又进行了一次封装。对于未找到相应的值的情况,会返回一个空的string。

    从第60行的调用,可以看出, _Load类是一个函数类,它提供的函数重载的接口应为:

    void operator() (PropMap& props, std::istream& is, const char* section);

    其中PropMap就是map类,在前面第37行已经有定义。显然,它的功能是从 is 流中,读入 section 节的字串名称/值数据,存放到 props 容器中。 下面是读ini文件格式的装载类的定义和实现:

    • IniProps的定义头文件:

    /* xtl/IniProps.h
      Author: ZhangTao
      Date: June 28, 2009
    */

    # ifndef IniProps_h
    # define IniProps_h

    # include       "xtl/Properties.h"

    namespace xtl  {

    class IniPropsLoad  {
      public:
        void operator() (PropMap& props, std::istream& is, const char* section);
    };

    typedef Properties<IniPropsLoad> IniProps;

    } // end of <namespace xtl>

    • IniProps的实现原程序文件

      1 /* IniPropsLoad.cpp
      2   Author: ZhangTao
      3   Date: Nov 6, 2008
      4 */
      5 
      6 # include       "xtl/utilfunc.h"
      7 # include       "xtl/IniProps.h"
      8 
      9 namespace xtl {
    10 
    11 DeclareThisFile;
    12 
    13 void IniPropsLoad::operator() (PropMap& props,
    14                 std::istream& is, const char* section)
    15 {
    16   char  inbuf[256];
    17 
    18   if ( !is )
    19     ThrowUtilExceptWithSource("can not read input stream", "");
    20 
    21   if ( !isEmptyStr(section) )        {
    22     char sec[64];
    23 
    24     int slen = sprintf(sec, "[%.60s]", section);
    25 
    26     while( is.getline(inbuf, sizeof inbuf) &&
    27                 (strncmp(inbuf, sec, slen) != 0) );
    28 
    29     if ( !is )
    30       ThrowUtilExceptWithSource("can not found section <%s>", section);
    31   }
    32 
    33   while( is.getline(inbuf, sizeof inbuf) && (inbuf[0] != '[') )  {
    34     // skip remark or space line
    35     if ( isSpaceLine(inbuf) )

    36       continue;
    37 
    38     char key[64];
    39     char val[128];
    40 
    41     if ( sscanf(inbuf, "%63[^=/t ] = %127[^/n]/n", key, val) > 1 )
    42       props.insert(make_pair(std::string(key), std::string(val)));
    43   }
    44 }
    45 
    46 }  // end of <namespace xtl>
    47

    第11行的DeclareThisFile是配合19行和30行的ThrowUtilExceptWithSource的调用使用的。这个抛出异常的定义在xtl/except.h文件中,在后面的文章里将会给出。

    第21行的isEmptyStr及第35行的isSpaceLine是在xtl/utilfunc.h中定义的。内容如下:

    inline bool isEmptyStr(const char *str)   { return *str == '/0'; }
    inline bool isSpaceLine(const char *line) {
      return (line[0] == '/0') || (line[0] == '#') || (line[0] == '/n')
                    || (line[0] == '/r');
    }

    根据名称就可判断 isEmptyStr是判断是否是空字符串。isSpace是判断是否是空行字符串。对于起始为#字符的也认为是空行,以便于可以在文件里面使用#行开始写注释信息。

    第21行到第31行是找到含有section 部的提示行。接着33行到44行是读入信息内容。在41行使用标准C库函数sscanf读入键名称和键值对。然后使用map容器的insert方法将内容插入到容器中。其中make_pair是stl库中将一对类组合成一个成员类的模版函数,正好适合map成员类的产生。这些内容可以参考C++的书籍。

    将IniPropsLoad类作为Properties类的模版参数,便可以产生一个使用的Propertie类了。如上面xtl/IniProps.h中定义的:

    typedef Properties<IniPropsLoad> IniProps;

    这种将Properties设计成模版类,以便于提供不同的读取数据内容的装载类,提高了Properties的通用性和可重用性。

    相应的测试程序修改如下:

    # include       "xtl/IniProps.h"

    int
    main(int argc, char* argv[])
    {
      const char* sec;

      if ( argc > 1 )
        sec = argv[1];
      else
        sec = "";

      xtl::IniProps prop(std::cin, sec);

      prop.list(std::cout);

      if ( argc > 2 )
        std::cout << "Key:<" << argv[2] << "> Value:" <<
                    prop.getProperty(argv[2]) << "/n";

      return 0;
    }

    测试的结果见上一篇介绍。如上一篇所介绍的,上面的prop.list(std::cout)也可以使用 std::cout << prop替代。另外,由于Properties类继承了stl库中的map类,还可以调用map类的方法,以满足其他特别的需求。比如,可以调用clear方法清除Properties类中的数据,然后再调用load重新装载数据。

    自我欣赏一下,感觉这个类的设计精巧、实用、灵活。你觉得呢?欢迎给出建议。有不明白的地方也可以提问。

    计划在下一篇,再介绍一下读取XML格式文件的Properties的实现。进一步感受一下其灵活性和可扩展性。

    本篇在前两篇的基础上,进一步给出XML格式文件装载的Properties类的实现。如前所述,正是因为我们将Properties设计成为一个模版类,使得装载的过程和方式称为模版的参数,使得Properties成为一个可以适应装载不同方式和格式的配置文件信息的实用类。在前一篇,我们实现了ini文件格式的装载。本篇我们来实现xml格式的装载。

    首先来说明一下我们规定的xml的格式。下面是一个实际的xml格式的配置属性信息内容,文件名称为 props.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd" >

    <prop>
    <node01>
           <comment>node01 configure</comment>
           <entry key="host">127.0.0.1</entry>
           <entry key="port">3001</entry>
           <entry key="nodeid">101-12345</entry>
           <entry key="acno">100000000000</entry>
           <entry key="teller">Auter01</entry>
           <entry key="market">201</entry>
           <entry key="quantity">1234</entry>
           <entry key="ctimeout">6000</entry>
    </node01>

    <node02>
           <comment>node02 configue</comment>,
           <entry key="host">127.0.0.1</entry>
           <entry key="port">3002</entry>
           <entry key="nodeid">102-12345</entry>
           <entry key="acno">100000000000</entry>
           <entry key="teller">Auter02</entry>
           <entry key="market">201</entry>
           <entry key="quantity">1234</entry>
           <entry key="ctimeout">6000</entry>
    </node02>

    </prop>

    在上面的内容中,根结点prop只是表示一个xml数据的开始。第二层结点为Properties的Section名称。第三层结点中的entry名称的结点为各个属性结点。其中的key属性的值为Properties的关键字名称,结点的实际内容为对应这个这个关键字的实际的值。

    现在,我们来实现对上面格式的xml文件的装载实现。如IniProps的实现一样,我们首先定义一个XMLProps的类。下面是这个类的定义头文件 xtl/XMLProps.h:


    2 /* xtl/XMLProps.h
    3   Author: ZhangTao
    4   Date: Nov 6, 2008
    5 */

    7 # ifndef XMLProps_h
    8 # define XMLProps_h

    10 # include       "xtl/Properties.h"
    11 
    12 namespace xtl  {
    13 
    14 class XMLPropsLoad  {
    15   public:
    16     void operator() (PropMap& props, std::istream& is, const char* section);
    17 };
    18 
    19 typedef Properties<XMLPropsLoad> XMLProps;
    20 
    21 const char* const XML_ENTRY = "entry";          // Property node entry name 
    22 const char* const XML_KEY = "key";              // Property key name
    23 const char* const DEFAULT_SECTION = "default";  // default section
    24 
    25 } // end of <namespace xtl>
    26 
    27 # endif /* end of <ifndef XMLPropsLoad_h> */
    28

    与上一篇的xtl/iniProps.h内容比较,可以看出,除了类的名称外,它们的内容几乎相同。在21行到23行定义了三个常量,用于装载函数中使用。比较上面的xml内容,可以看出XML_ENTRY对应的是属性结点的名称;XML_KEY对应的是关键字的xml结点属性名称。DEFAULT_SECTION是定义了缺省的属性部的名称。

    实现的文件名称为 XMLPropsLoad.cpp。其中使用了开源的xml2的函数库作为xml的解析工具。内容如下:

      1 /* XMLProps.cpp
      2   Author: ZhangTao
      3   Date: Nov 6, 2008
      4 */
      5 
      6 # include       <sstream>
      7 
      8 # include       "libxml/parser.h"
      9 # include       "xtl/utilfunc.h"
    10 # include       "xtl/XMLProps.h"
    11 
    12 namespace xtl {
    13 
    14 DeclareThisFile;
    15 
    16 void XMLPropsLoad::operator() (PropMap& props, std::istream& is,
    17                 const char* section)
    18 {
    19   std::stringstream  iss;
    20 
    21   iss << is.rdbuf();
    22 
    23   const std::string& xmlStr = iss.str();
    24 
    25   xmlDocPtr xdoc = xmlReadMemory(xmlStr.c_str(), xmlStr.size(), "Prop",  0, 0);
    26 
    27   if ( xdoc == 0 )
    28     ThrowUtilExceptWithSource("Invalid XML stream", "");
    29 
    30   // goto root level
    31   xmlNodePtr curNode = xmlDocGetRootElement(xdoc);
    32 
    33   // goto <section> level
    34   if ( (curNode == 0) || ((curNode = curNode->xmlChildrenNode) == 0) )
    35     ThrowUtilExceptWithSource("Empty XML stream", ""); 
    36 
    37 
    38   if ( isEmptyStr(section) )
    39     section = DEFAULT_SECTION;
    40 
    41   while( (curNode != 0) && (xmlStrcmp(curNode->name,
    42         (const xmlChar*)section) != 0) )
    43     curNode = curNode->next;
    44 
    45   if ( curNode == 0 )
    46     ThrowUtilExceptWithSource("can not found <%s> node", section);
    47 
    48   // goto <Property> level
    49   curNode = curNode->xmlChildrenNode;
    50 
    51   while( curNode != 0 )      {
    52     if ( xmlStrcmp(curNode->name, (const xmlChar*)XML_ENTRY) == 0 )    {
    53       xmlChar* key = xmlGetProp(curNode, (const xmlChar*)XML_KEY);
    54       xmlChar* val = xmlNodeGetContent(curNode);
    55 
    56       props.insert(make_pair(std::string((const char*)key),
    57                         std::string((const char*)val)));
    58       xmlFree(key);
    59       xmlFree(val);
    60     }
    61     curNode = curNode->next;
    62   }
    63 }
    64 
    65 } // end of <namespace stl>
    66

    这里19行定义了一个字串流。21行通过输入流的rdbuf调用将输入流参数的内容读入到了字串流。通过字串流得到一个string类,从而又可以得到一个C字符串内容,满足了xml库读入内容的解析函数xmlReadMemory的参数要求。这些都属于C++标准库的操作,可以参考C++的有关输入输出流部分。接下来的过程便是xml解析函数的操作。56行是得到有关的属性值对后的插入操作。

    同样的,我们编写一个测试程序tst-xmlprops.cpp如下:

    # include       "xtl/XMLProps.h"

    int
    main(int argc, char* argv[])
    {
      const char* sec;

      if ( argc > 1 )
        sec = argv[1];
      else
        sec = "";

      xtl::XMLProps prop(std::cin, sec);

      prop.list(std::cout);

      if ( argc > 2 )
        std::cout << "Key:<" << argv[2] << "> Value:" <<
                    prop.getProperty(argv[2]) << "/n";

      return 0;
    }

    编译成目标码 tst-xmlprops,使用上面的xml文件作为测试内容。

    # ./tst-xmlprops node01 port <props.xml
    acno = 100000000000
    ctimeout = 6000
    host = 127.0.0.1
    market = 201
    nodeid = 101-12345
    port = 3001
    quantity = 1234
    teller = Auter01
    Key:<port> Value:3001

    至此,大功告成。

    下一篇,将介绍一个Properties类的应用实例。

  • 相关阅读:
    多个类定义attr属性重复的问题:Attribute "xxx" has already been defined
    好用的批量改名工具——文件批量改名工具V2.0 绿色版
    得到ImageView中drawable显示的区域的计算方法
    得到view坐标的各种方法
    实现类似于QQ空间相册的点击图片放大,再点后缩小回原来位置
    Material Designer的低版本兼容实现(五)—— ActivityOptionsCompat
    Android 自带图标库 android.R.drawable
    解决 Attempting to destroy the window while drawing!
    解决Using 1.7 requires compiling with Android 4.4 (KitKat); currently using API 4
    Material Designer的低版本兼容实现(四)—— ToolBar
  • 原文地址:https://www.cnblogs.com/DjangoBlog/p/5806038.html
Copyright © 2011-2022 走看看