http://www.leoox.com/?p=311

程序肯定需要一份配置文件,要不然,自己的程序不是“可配置”的,自己都不好意思往“高大上”靠拢。言归正传,以前自己写代码,配置文件的读写都是各式各样的,有用过xml,有用过其他项目copy过来的。看开源代码的时候,也是各式各样的,比如redis的,Nginx等等。有时候就在想,配置文件的解析还真是麻烦,要自己处理一堆的字符串,有空的时候自己整理一下Nginx的源码,复用Nginx的配置代码,加强自己的代码库。但最近才发现,原来已经有一个很优秀的C/C++配置库libconfig一直在等着我了。

认识libconfig

libconfig库的官方网站在:http://www.hyperrealm.com/libconfig/

确实是非常优秀的C/C++配置库,我们程序员完全可以从解析字符串的“苦力”中解脱出来。多复杂的配置项,都能满足,来看看。

A configuration consists of a group of settings, which associate names with values. A value can be one of the following:

  • A scalar value: integer, 64-bit integer, floating-point number, boolean, or string
  • An array, which is a sequence of scalar values, all of which must have the same type
  • A group, which is a collection of settings
  • A list, which is a sequence of values of any type, including other lists

简单直译一下:

一个配置项,可以理解为我们最常见的key-value的形式。key就是你的配置的名字了。那优秀就优秀在value上了。value支持的类型有:

1、常见的数据类型:

整数(int):可以用10进制和16进制表示。0x打头的数字libconfig会自动解析为16进制的数字。

64位整数(int64_t):在数字的后面加上L即可。

浮点数(float):个人不太喜欢用这个类型。

布尔数(bool):true或者false。不区分大小写。

字符串(string):这个字符串非常强大。

a、支持转义字符\\’, ‘\f’, ‘\n’, ‘\r’,‘\x’ and ‘\t’。

b、相邻的字符串libconfig会自动拼接。这样太常的内容,我们可以多行来写,可读性非常好。比如:

example = “hello world”;  等价于

example = “hello”

” world”;

【注意】

       我们可以使用’=’,也可以使用’:’来作为赋值号。既然是C/C++程序员,还是使用’=’号看得舒服一些。

和C/C++的注释一样,/**/就是跨行的注释。 //就是单行注释。当然还支持脚本语言的注释符号#,#也是单行注释。但是特殊的是,如果注释符在双引号中使用,那就不再是注释符了,libconfig会解析为正常的明文。

2、数组结构。和平常我们使用的数组是一样一样的,数组的各个元素都必须是相同的数据类型。

3、群组结构。这个可以理解为一个容器。这个容器里面,我们可以放置很多个配置项。当然这些配置项的value也可以继续是群组。

4、列表结构。这个列表和我们C++常用的STL里的list结构可不太一样。这个列表结构里面的元素不要求具备相同的数据类型,元素1是int,元素2可以是string,元素3可以是数组,元素4可以是一个群组,元素5可以是另一个列表。

可以说,正是因为value的多姿多彩,才给了我们程序员无限的发挥空间。通过群组结构和列表结构,我们可以很方便灵活的进行各种变态的配置读取。除了读取配置,可不要忘记了libconfig还有两只手的哦:必要的时候,我们可以把内存里面的一些值,通过libconfig生成一份标准的配置文件。

体验libconfig

动手用libconfig进行一个hello world的配置吧!把value支持的所有数据类型都用上,加深理解。

example.conf

 
 
 
 
 
 

C++

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* 这个叫Settings */
version = "1.0.0.0";
 
/* 这个叫Groups */
log = {
  log_path = "../logs/sys.log";  /* 日志文件路径 */
  log_size = 1024000000L;        /* 日志文件大小 */
  log_level = 20000;             /* 日志等级 */
};
 
/* 这个叫Lists */
// 业务服务器列表
server = (
  {
    addr = "10.1.88.1";
    port = 9090;
  },
  {
    addr = "10.1.88.2";
    port = 9090;
  },
  {
    addr = "10.1.88.3";
    port = 9090;
  }
);
 
// 测试配置
test = {
  // 这个是数组结构。
  uin = [10086L, 10087L, 10088L, 10089L];  /* 测试号码 */
  /* 测试服务器 */
  server = {
    addr = "10.1.99.1";
    port = 9090;
  };
};

值得说明的是,libconfig是通过路径来读取某一个配置的。比如log.log_path这个路径对应的是log_path这个配置项,

server.[0].addr这个路径对应的是业务服务器列表的第一个元素里面的addr这个配置项。

libconfig的代码样例

不写一段hello world的代码,是算不上真正接触了libconfig的。libconfig提供了C和C++的API。先用C++来爽一下吧。

首先就是要下载安装libconfig的库。这个很简单,到官网下载,然后./configure & make & make install就可以了。

 
 
 
 
 
 

C++

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <libconfig.h++>
 
using namespace std;
using namespace libconfig;
 
/* g++ -o demo demo.cpp -I/home/leoox/local/libconfig-1.4.9/include/ /home/leoox/local/libconfig-1.4.9/lib/libconfig++.a */
int main(int argc, char** argv)
{
  Config cfg;
 
  /*
    解析配置文件。
  */
  try
  {
    cfg.readFile("example.conf");
  }
  catch(const FileIOException &fioex)
  {
    std::cerr << "I/O error while reading file." << std::endl;
    return(EXIT_FAILURE);
  }
  catch(const ParseException &pex)
  {
    std::cerr << "Parse error at " << pex.getFile() << ":" << pex.getLine()
              << " - " << pex.getError() << std::endl;
    return(EXIT_FAILURE);
  }
 
  /* 从配置文件中,得到version这个配置项的值。 */
  try
  {
    string version = cfg.lookup("version");
    cout << "version: " << version << endl << endl;
  }
  catch(const SettingNotFoundException &nfex)
  {
    cerr << "No 'version' setting in configuration file." << endl;
  }
 
  /* 从配置文件中,得到日志相关配置值 */
  try
  {
    string log_path = cfg.lookup("log.log_path");
    cout << "log_path: " << log_path << endl;
 
    int64_t log_size = cfg.lookup("log.log_size");
    cout << "log_size: " << log_size << endl;
 
    int log_level = cfg.lookup("log.log_level");
    cout << "log_level: " << log_level << endl << endl;
  }
  catch(const SettingNotFoundException &nfex)
  {
    cerr << "log setting mistake in configuration file." << endl;
  }
 
  try
  {
    const Setting &server = cfg.lookup("server");
    int count = server.getLength();
    cout << "server.count = " << count << endl;
 
    for (int i = 0; i < count; i++)
    {
      string addr = "";
      int port = 0;
      if (!server[i].lookupValue("addr", addr)
          || !server[i].lookupValue("port", port))
      {
        cerr << "lookupValue error" << endl;
        continue;
      }
 
      cout << "server[" << i << "] = " << addr << ":" << port << endl;
    }
 
    {
      string addr = "";
      int port = 0;
      if (!cfg.lookupValue("server.[0].addr", addr)
          || !cfg.lookupValue("server.[0].port", port)) {
        cerr << "lookupValue 'server.[0].addr' error" << endl;
      }
      else
        cout << "server[0] = " << addr << ":" << port << endl << endl;
    }
  }
  catch(const SettingNotFoundException &nfex)
  {
    cerr << "server setting mistake in configuration file." << endl;
  }
 
  try
  {
    const Setting& root = cfg.getRoot();
    const Setting &uin = root["test"]["uin"];
    int count = uin.getLength();
    cout << "uin.count = " << count << endl;
 
    const Setting &test = cfg.lookup("test");
    const Setting &test2 = cfg.lookup("test.uin");
    for (int i = 0 ; i < count; i++)
    {
      int64_t u = test["uin"][i];
      int64_t uu = uin[i];
      int64_t uuu = test2[i];
      cout << "uin[" << i << "] = " << u << ", " << uu << ", " << uuu << endl;
    }
 
    const Setting &server = root["test"]["server"];
    string addr = "";
    int port = 0;
    if (!server.lookupValue("addr", addr)
        || !server.lookupValue("port", port))
    {
      cerr << "test server lookupValue error" << endl;
    }
    else
      cout << "test server = " << addr << ":" << port << endl << endl;
  }
  catch(const SettingNotFoundException &nfex)
  {
    cerr << "test setting mistake in configuration file." << endl;
  }
 
  return 0;
}

编译和运行一下:

 
 
 
 
 
 

C++

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[leoox@Thrift config]$ g++ -o demo demo.cpp -I/home/leoox/local/libconfig-1.4.9/include/ /home/leoox/local/libconfig-1.4.9/lib/libconfig++.a
[leoox@Thrift config]$ ./demo
version: 1.0.0.0
 
log_path: ../logs/sys.log
log_size: 1024000000
log_level: 20000
 
server.count = 3
server[0] = 10.1.88.1:9090
server[1] = 10.1.88.2:9090
server[2] = 10.1.88.3:9090
server[0] = 10.1.88.1:9090
 
uin.count = 4
uin[0] = 10086, 10086, 10086
uin[1] = 10087, 10087, 10087
uin[2] = 10088, 10088, 10088
uin[3] = 10089, 10089, 10089
test server = 10.1.99.1:9090

C/C++服务器开发的必备利器–libconfig的更多相关文章

  1. C# WebApi过滤器(开发接口必备利器)

    在WEB Api中,引入了面向切面编程(AOP)的思想,在某些特定的位置可以插入特定的Filter进行过程拦截处理.引入了这一机制可以更好地践行DRY(Don’t Repeat Yourself)思想 ...

  2. C# WebApi 过滤器的使用开发接口必备利器

    在WEB Api中,引入了面向切面编程(AOP)的思想,在某些特定的位置可以插入特定的Filter进行过程拦截处理.引入了这一机制可以更好地践行DRY(Don’t Repeat Yourself)思想 ...

  3. Fiddler无所不能——之测试开发攻城狮必备利器

    Fiddler无所不能——之测试开发攻城狮必备利器 1.模拟真实网络环境4g网.3g网络.2g网络.弱网.请求超时 开启弱网Rules——Performance——勾选Simulate Modem S ...

  4. (转)Terraform,自动化配置与编排必备利器

    本文来自作者 QingCloud实践课堂 在 GitChat 上分享 「Terraform,自动化配置与编排必备利器」 Terraform - Infrastructure as Code 什么是 T ...

  5. 【特别推荐】Web 开发人员必备的经典 HTML5 教程

    对于我来说,Web 前端开发是最酷的职业之一,因为你可以用新的技术发挥,创造出一些惊人的东西.唯一的问题是,你需要跟上这个领域的发展脚步,因此,你必须不断的学习,不断的前进.本文将分享能够帮助您快速掌 ...

  6. Android开发工具综述,开发人员必备工具

    安卓开发工具汇总.开发者必备.安卓开发过程中须要用到各种工具,作为一名安卓开发者,有木有感到亚历山大,那么多工具! 今天给大家汇总了一下安卓开发工具,安卓开发者必备利器. 1.Draw 9-Patch ...

  7. SQL点滴17—使用数据库引擎存储过程,系统视图查询,DBA,BI开发人员必备基础知识

    原文:SQL点滴17-使用数据库引擎存储过程,系统视图查询,DBA,BI开发人员必备基础知识 在开发过程中会遇到需要弄清楚这个数据库什么时候建的,这个数据库中有多少表,这个存储过程长的什么样子等等信息 ...

  8. 利用Nginx轻松实现Ajax的跨域请求(前后端分离开发调试必备神技)

    利用Nginx轻松实现浏览器中Ajax的跨域请求(前后端分离开发调试必备神技) 前言 为什么会出现跨域? 造成跨域问题的原因是因为浏览器受到同源策略的限制,也就是说js只能访问和操作自己域下的资源,不 ...

  9. Web开发与设计之Google兵器谱-Web开发与设计利器

    Web开发与设计之Google兵器谱-Web开发与设计利器 博客分类: Java综合 WebGoogleAjaxChromeGWT 笔者是个Java爱好者也是用Java进行web开发的工作者.平时笔者 ...

随机推荐

  1. SNMP相关命令

    SNMP的相关命令使用方法: snmpdelta 一直监视SNMP变量中的变化 linux:~ # snmpdelta -c public -v 1 -Cs -CT localhost IF-MIB: ...

  2. [USACO18FEB]Snow Boots S

    提供一种无脑DP做法 题目中大概有这么些东西:位置,穿鞋,跑路 数据小,那么暴力开数组暴力DP吧 设dp[i][j]表示穿着鞋子j,到达位置i是否可行 无脑转移 枚举位置,正在穿哪双鞋,换成哪双走出去 ...

  3. B树 B+树 红黑树

    B-Tree(B树) 具体讲解之前,有一点,再次强调下:B-树,即为B树.因为B树的原英文名称为B-tree,而国内很多人喜欢把B-tree译作B-树,其实,这是个非常不好的直译,很容易让人产生误解. ...

  4. GreenPlum学习笔记:基础知识

    一.介绍 GreenPlum分布式数据仓库,大规模并行计算技术. 无共享/MPP核心架构 Greenplum数据库软件将数据平均分布到系统的所有节点服务器上,所以节点存储每张表或表分区的部分行,所有数 ...

  5. Knockout.Js官网学习(模版绑定)

    模板绑定器 如今页面结构越来越复杂,仅仅依靠foreach已经不足以我们的使用,这个时候我们就需要模板的存在,模板的优点自然很多,首先会让页面整洁,同时修改起来也可以方面的定位,最重要的是ko可以条件 ...

  6. bzoj 2752

    2752 思路: 线段树: 代码: #include <cstdio> #include <cstring> #include <iostream> #includ ...

  7. 【LOJ】#2447. 「NOI2011」兔兔与蛋蛋的游戏

    题解 对于75分来说,操作肯定不会成环,可以暴搜 看成空格在移动,空格移动到原来的位置肯定经历了偶数个格子,但是操作的人是两个不同的人,所以肯定不会成环 对于满分做法,要找到一种更好的方式判先手是否会 ...

  8. 关于JavaScript变量提升

    请看如下代码: console.log(a); var a = 2; 输入结果会是什么?  请说出理由 可能会有三种答案: 1.2 2.抛出ReferenceError异常 3.undifined 对 ...

  9. IOS和Android音频开发总结

    最近因为项目需要对声音进行变声,所以边学习边做,发现音频的处理思路并不难,但是做起来还是有些繁琐的(比预期的) 趁着脑子还发热,赶紧把思路总结一下,记录下来. 主要讲三个部分 1,如何变声2,安卓实现 ...

  10. .Net中的IO

    C#中的IO 本文环境为: Win 10 VS 2015 .net Framework 版本 4.0 File 类 File 类是一个工具类,可以用来判断文件是否存在和方便的创建文件, 读取.写入等操 ...