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:
1
2 /* xtl/XMLProps.h
3 Author: ZhangTao
4 Date: Nov 6, 2008
5 */
6
7 # ifndef XMLProps_h
8 # define XMLProps_h
9
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类的应用实例。
Properties --- C++读配置信息的类(一)的更多相关文章
- Properties --- C++读配置信息的类
http://blog.csdn.net/billow_zhang/article/details/4304980 在开发实践中,积累了一些通用的C++ 类库,在此写出来给大家分享.也希望能给出更好的 ...
- java读取properties文件的配置信息
项目开发中,我们一般来向 application.properties 文件中放一些全局配置变量,以便程序中读取使用,本篇内容来演示从properties配置文件中读取键值. 当然,我们不一定写入 a ...
- properties文件读取配置信息
public static void main(String[] args){ String printerName=""; String path = "C:\\Bar ...
- spring boot mybatis XML文件读取properties配置信息
配置文件application.properties中相关配置信息可以在部署以后修改,引用配置信息可以在代码和mybatis的映射文件中 1.JAVA代码 可以通过变量去读取 application. ...
- Hibernate 系列 04 - Hibernate 配置相关的类
引导目录: Hibernate 系列教程 目录 前言: 通过上一篇的增删改查小练习之后,咱们大概已经掌握了Hibernate的基本用法. 我们发现,在调用Hibernate API的过程中,虽然Hib ...
- 教你如何利用分布式的思想处理集群的参数配置信息——spring的configurer妙用
引言 最近LZ的技术博文数量直线下降,实在是非常抱歉,之前LZ曾信誓旦旦的说一定要把<深入理解计算机系统>写完,现在看来,LZ似乎是在打自己脸了.尽管LZ内心一直没放弃,但从现状来看,需要 ...
- configsections規範配置信息
對於小型項目,配置信息可以通过appSettings进行配置,而如果配置信息太多,appSettings显得有些乱,而且在开发人员调用时,也不够友好,节点名称很容易写错,这时,我们有几种解决方案 1 ...
- AspNet Core 程序写入配置信息并再次读取配置信息
1.首先创见Core控制台应用程序 并且引入 AspNetCore.All 首先我们写入配置信息:直接代码如下 //配置信息的根对象 public static IConfigurationRoo ...
- 基础才是重中之重~用好configSections让配置信息更规范
对于小型项目来说,配置信息可以通过appSettings进行配置,而如果配置信息太多,appSettings显得有些乱,而且在开发人员调用时,也不够友好,节点名称很容易写错,这时,我们有几种解决方案 ...
随机推荐
- 分享7款顶级的CSS3动画特效
1.CSS3 SVG文字背景动画 超酷的文字特效 今天我们来分享一款基于CSS3和SVG的文字特效,文字的背景可以定义一些动画特效,在背景动画播放的同时,我们还可以选中文字.本示例让文字背景展示水波和 ...
- Bring up a website by wordpress
WORDPRESS WordPress is web software you can use to create a beautiful website or blog, which is both ...
- tar 「解/壓」 包
1.tar: short read tar壓縮包文件發生了損壞,或者说不完整. 最近,在寫下載程序的時候,發生了這個問題.初步猜測文件寫完沒有關閉,沒有寫完.
- IOS数据解析JSON
//非原创 作为一种轻量级的数据交换格式,json正在逐步取代xml,成为网络数据的通用格式. 有的json代码格式比较混乱,可以使用此“http://www.bejson.com/”网站来进行JSO ...
- android SDK启动的错误
1. AVD Manager.exe : failed to execute tools android.bat 请把AVD Manager.exe复制到跟 SDK Manager.exe的相同目录下 ...
- 应用js改变问章字体大小
刚来公司的时候领导给分配的都是一些简单的简单的简单的.....任务 一次叫我把文章的字体大小变换功能写出来.在网上搜了很多都不管用!不过功夫不负有心人还是被我找到了!拿出来分享下! <scrip ...
- Python-Day3 Python基础进阶之集和/文件读写/函数
一.集和 集合是一个无序的,不重复的数据组合,它的主要作用如下: 去重,把一个列表变成集合,就自动去重了 关系测试,测试两组数据之前的交集.差集.并集等关系 1.创建集合 >>> s ...
- SQL多表连接
在KS系统中分配好权限以后,在用户登录的时候就要通过用户查到角色,通过角色查到界面,界面又属于某个菜单,一共要查4个表.并且不能有重复的记录这个时候就用到了SQL的内连接.SQL的多表连接很方便,以前 ...
- 编辑器未包含main类型解决方法
将文件移到 src 这个 Java Source Folder 下面去,现在在外面的 java 文件不会被当成一个需要编译的类,eclipse 不会编译 Java Source Folder 外面的任 ...
- 使用WinSetupFromUSB来U盘安装WINDOWS2003
今天用UltraISO制作WINDOWS2003的U盘的安装启动,在安装系统的时候发现错误提示“INF file txtsetup.sif is corrupt or missing .status ...