http://www.leoox.com/?p=311
程序肯定需要一份配置文件,要不然,自己的程序不是“可配置”的,自己都不好意思往“高大上”靠攏。言歸正傳,以前自己寫代碼,配置文件的讀寫都是各式各樣的,有用過xml,有用過其他項目copy過來的??撮_源代碼的時候,也是各式各樣的,比如redis的,Nginx等等。有時候就在想,配置文件的解析還真是麻煩,要自己處理一堆的字符串,有空的時候自己整理一下Nginx的源碼,復(fù)用Nginx的配置代碼,加強自己的代碼庫。但最近才發(fā)現(xiàn),原來已經(jīng)有一個很優(yōu)秀的C/C++配置庫libconfig一直在等著我了。
認(rèn)識libconfig
libconfig庫的官方網(wǎng)站在:http://www.hyperrealm.com/libconfig/
確實是非常優(yōu)秀的C/C++配置庫,我們程序員完全可以從解析字符串的“苦力”中解脫出來。多復(fù)雜的配置項,都能滿足,來看看。
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就是你的配置的名字了。那優(yōu)秀就優(yōu)秀在value上了。value支持的類型有:
1、常見的數(shù)據(jù)類型:
整數(shù)(int):可以用10進(jìn)制和16進(jìn)制表示。0x打頭的數(shù)字libconfig會自動解析為16進(jìn)制的數(shù)字。
64位整數(shù)(int64_t):在數(shù)字的后面加上L即可。
浮點數(shù)(float):個人不太喜歡用這個類型。
布爾數(shù)(bool):true或者false。不區(qū)分大小寫。
字符串(string):這個字符串非常強大。
a、支持轉(zhuǎn)義字符\\’, ‘\f’, ‘\n’, ‘\r’,‘\x’ and ‘\t’。
b、相鄰的字符串libconfig會自動拼接。這樣太常的內(nèi)容,我們可以多行來寫,可讀性非常好。比如:
example = “hello world”; 等價于
example = “hello”
” world”;
【注意】
我們可以使用’=’,也可以使用’:’來作為賦值號。既然是C/C++程序員,還是使用’=’號看得舒服一些。
和C/C++的注釋一樣,/**/就是跨行的注釋。 //就是單行注釋。當(dāng)然還支持腳本語言的注釋符號#,#也是單行注釋。但是特殊的是,如果注釋符在雙引號中使用,那就不再是注釋符了,libconfig會解析為正常的明文。
2、數(shù)組結(jié)構(gòu)。和平常我們使用的數(shù)組是一樣一樣的,數(shù)組的各個元素都必須是相同的數(shù)據(jù)類型。
3、群組結(jié)構(gòu)。這個可以理解為一個容器。這個容器里面,我們可以放置很多個配置項。當(dāng)然這些配置項的value也可以繼續(xù)是群組。
4、列表結(jié)構(gòu)。這個列表和我們C++常用的STL里的list結(jié)構(gòu)可不太一樣。這個列表結(jié)構(gòu)里面的元素不要求具備相同的數(shù)據(jù)類型,元素1是int,元素2可以是string,元素3可以是數(shù)組,元素4可以是一個群組,元素5可以是另一個列表。
可以說,正是因為value的多姿多彩,才給了我們程序員無限的發(fā)揮空間。通過群組結(jié)構(gòu)和列表結(jié)構(gòu),我們可以很方便靈活的進(jìn)行各種變態(tài)的配置讀取。除了讀取配置,可不要忘記了libconfig還有兩只手的哦:必要的時候,我們可以把內(nèi)存里面的一些值,通過libconfig生成一份標(biāo)準(zhǔn)的配置文件。
體驗libconfig
動手用libconfig進(jìn)行一個hello world的配置吧!把value支持的所有數(shù)據(jù)類型都用上,加深理解。
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 */ // 業(yè)務(wù)服務(wù)器列表 server = ( { addr = "10.1.88.1"; port = 9090; }, { addr = "10.1.88.2"; port = 9090; }, { addr = "10.1.88.3"; port = 9090; } ); // 測試配置 test = { // 這個是數(shù)組結(jié)構(gòu)。 uin = [10086L, 10087L, 10088L, 10089L]; /* 測試號碼 */ /* 測試服務(wù)器 */ server = { addr = "10.1.99.1"; port = 9090; }; }; |
值得說明的是,libconfig是通過路徑來讀取某一個配置的。比如log.log_path這個路徑對應(yīng)的是log_path這個配置項,
server.[0].addr這個路徑對應(yīng)的是業(yè)務(wù)服務(wù)器列表的第一個元素里面的addr這個配置項。
libconfig的代碼樣例
不寫一段hello world的代碼,是算不上真正接觸了libconfig的。libconfig提供了C和C++的API。先用C++來爽一下吧。
首先就是要下載安裝libconfig的庫。這個很簡單,到官網(wǎng)下載,然后./configure & make & make install就可以了。
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; } /* 從配置文件中,得到日志相關(guān)配置值 */ 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; } |
編譯和運行一下:
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 |