Properties --- C++读配置信息的类

来源:互联网 发布:seo外链怎么发 编辑:程序博客网 时间:2024/04/30 06:52

在开发实践中,积累了一些通用的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函数了。 

 

在第一部分中,列出了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的实现。进一步感受一下其灵活性和可扩展性。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/billow_zhang/archive/2009/06/30/4311317.aspx

 

原创粉丝点击