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