• 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类的应用实例。

     
     
  • 相关阅读:
    shell编程基础干货
    HIVE的高级操作
    Linux service,挂载,定时任务等常用服务
    Linux(二)高级文本处理
    Linux基本使用命令
    07-MySQL 架构介绍
    06-Re: 视图&过程&触发器
    05-安装 MySQL5.7
    [04] 继承&聚合&war
    [03] 仓库&生命周期&插件目标
  • 原文地址:https://www.cnblogs.com/DjangoBlog/p/6099360.html
Copyright © 2020-2023  润新知