配置文件很重要,INI 太弱,XML 太繁复,Linux *.conf 很酷。
找了好几种相关的类库,发觉还是 hyperrealm libconfig 最强大最好用,相关细节可参考 官方手册。
源中的版本是 1.3.2-1,也可以去官方文章下载最新版本。
完全类脚本化的配置语法,支持注释、包含、简单配置、数组、列表以及非常像类的组。
test.conf
1. Path
直接用多级路径读取目标值,这是最简单的做法。注意区分参数中 path 和 name 的区别,后者无法使用路径。
我们试试看。
输出:
2. Config_Setting
所有的 Group 和其 Member 都是 Config_Setting,我们可以用 config_lookup() 找出目标后,然后使用 Name 读取。
注意 config_setting_lookup_xxx() 只能使用 Member Name,而不是 Path。
利用相关的函数,我们还可以遍历 Array/List 的所有 Element。
输出:
当然,我们也可以用 config_lookup() 直接找到 app.user.tags,然后遍历。
输出:
上面的例子中,我们还可以直接用 lookup() 查找简单配置 app.user.code,然后用相关方法返回值,无需再次提供 Name。
Array/List 的内容可以是 Group,我们可以用 config_setting_get_elem() 获取指定序号的元素后继续操作。
3. Write
配置文件吗,增删改操作都要全乎。
为了方便查看,我直接 "保存" 到 stdout 了。
输出:
4. Q&A
(1) 调用 config_destroy(conf) 后,其分配的字符串会被全部释放,因此得自己注意 strcpy / strdup。
(2) 官方文档中标明了 "Libconfig is not thread-safe","Libconfig is not async-safe" ……
找了好几种相关的类库,发觉还是 hyperrealm libconfig 最强大最好用,相关细节可参考 官方手册。
源中的版本是 1.3.2-1,也可以去官方文章下载最新版本。
$ sudo apt-get install libconfig8 libconfig8-dev
完全类脚本化的配置语法,支持注释、包含、简单配置、数组、列表以及非常像类的组。
test.conf
# Example application configuration file title = "Test Application"; // scalar value version = 1; // int, int64, float, bool, string app: // group { user: { name = "Q.yuhen"; code = "xxx-xxx-xxx"; tags = ["t1", "t2", "t3"]; // array data = ( "Hello", 1234 ); // list } };
1. Path
直接用多级路径读取目标值,这是最简单的做法。注意区分参数中 path 和 name 的区别,后者无法使用路径。
int config_lookup_int (const config_t * config, const char * path, int * value) int config_lookup_int64 (const config_t * config, const char * path, long long * value) int config_lookup_float (const config_t * config, const char * path, double * value) int config_lookup_bool (const config_t * config, const char * path, int * value) int config_lookup_string (const config_t * config, const char * path, const char ** value)
我们试试看。
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> #include <libconfig.h> void scalar(config_t* conf) { char* title; config_lookup_string(conf, "title", &title); printf("title = %s;\n", title); int version; config_lookup_int(conf, "version", &version); printf("version = %d;\n", version); char* user_name; config_lookup_string(conf, "app.user.name", &user_name); printf("app.user.name = %s;\n", user_name); char* tag; config_lookup_string(conf, "app.user.tags.[2]", &tag); printf("app.user.tags[2] = %s;\n", tag); int data; config_lookup_int(conf, "app.user.data.[1]", &data); printf("app.user.data.[1] = %d;\n", data); } int main(int argc, char* argv[]) { config_t* conf = &(config_t){}; config_init(conf); config_read_file(conf, "test.conf"); scalar(conf); config_destroy(conf); return EXIT_SUCCESS; }
输出:
title = Test Application; version = 1; app.user.name = Q.yuhen; app.user.tags[2] = t3; app.user.data.[1] = 1234;
2. Config_Setting
所有的 Group 和其 Member 都是 Config_Setting,我们可以用 config_lookup() 找出目标后,然后使用 Name 读取。
config_setting_t * config_lookup (const config_t * config, const char * path) int config_setting_lookup_int (const config_setting_t * setting, const char * name, int * value) int config_setting_lookup_int64 (const config_setting_t * setting, const char * name, long long * value) int config_setting_lookup_float (const config_setting_t * setting, const char * name, double * value) int config_setting_lookup_bool (const config_setting_t * setting, const char * name, int * value) int config_setting_lookup_string (const config_setting_t * setting, const char * name, const char ** value)
注意 config_setting_lookup_xxx() 只能使用 Member Name,而不是 Path。
void group(config_t* conf) { config_setting_t* user = config_lookup(conf, "app.user"); char* code; config_setting_lookup_string(user, "code", &code); printf("user.code = %s;\n", code); }
利用相关的函数,我们还可以遍历 Array/List 的所有 Element。
void group(config_t* conf) { config_setting_t* user = config_lookup(conf, "app.user"); config_setting_t* tags = config_setting_get_member(user, "tags"); int count = config_setting_length(tags); int i; for (i = 0; i < count; i++) { printf("user.tags[%d] = %s;\n", i, config_setting_get_string_elem(tags, i)); } }
输出:
user.tags[0] = t1; user.tags[1] = t2; user.tags[2] = t3;
当然,我们也可以用 config_lookup() 直接找到 app.user.tags,然后遍历。
void group(config_t* conf) { config_setting_t* tags = config_lookup(conf, "app.user.tags"); int count = config_setting_length(tags); int i; for (i = 0; i < count; i++) { printf("user.tags[%d] = %s;\n", i, config_setting_get_string_elem(tags, i)); } printf("-----------------------\n"); config_setting_t* code = config_lookup(conf, "app.user.code"); printf("user.code = %s;\n", config_setting_get_string(code)); }
输出:
user.tags[0] = t1; user.tags[1] = t2; user.tags[2] = t3; ----------------------- user.code = xxx-xxx-xxx;
上面的例子中,我们还可以直接用 lookup() 查找简单配置 app.user.code,然后用相关方法返回值,无需再次提供 Name。
int config_setting_get_int (const config_setting_t * setting) long long config_setting_get_int64 (const config_setting_t * setting) double config_setting_get_float (const config_setting_t * setting) int config_setting_get_bool (const config_setting_t * setting) const char * config_setting_get_string (const config_setting_t * setting)
Array/List 的内容可以是 Group,我们可以用 config_setting_get_elem() 获取指定序号的元素后继续操作。
config_setting_t * config_setting_get_member (config_setting_t * setting, const char * name) config_setting_t * config_setting_get_elem (const config_setting_t * setting, unsigned int idx)
3. Write
配置文件吗,增删改操作都要全乎。
int config_setting_set_int (config_setting_t * setting, int value) int config_setting_set_int64 (config_setting_t * setting, long long value) int config_setting_set_float (config_setting_t * setting, double value) int config_setting_set_bool (config_setting_t * setting, int value) int config_setting_set_string (config_setting_t * setting, const char * value) config_setting_t * config_setting_set_int_elem (config_setting_t * setting, int idx, int value) config_setting_t * config_setting_set_int64_elem (config_setting_t * setting, int idx, long long value) config_setting_t * config_setting_set_float_elem (config_setting_t * setting, int idx, double value) config_setting_t * config_setting_set_bool_elem (config_setting_t * setting, int idx, int value) config_setting_t * config_setting_set_string_elem (config_setting_t * setting, int idx, const char * value) config_setting_t * config_setting_add (config_setting_t * parent, const char * name, int type) int config_setting_remove (config_setting_t * parent, const char * name) int config_setting_remove_elem (config_setting_t * parent, unsigned int idx) const char * config_setting_name (const config_setting_t * setting)
为了方便查看,我直接 "保存" 到 stdout 了。
void write(config_t* conf) { config_setting_t* user = config_lookup(conf, "app.user"); config_setting_t* name = config_setting_get_member(user, "name"); config_setting_t* tags = config_setting_get_member(user, "tags"); config_setting_t* data = config_setting_get_member(user, "data"); /* ----------------- Add ------------------- */ config_setting_t* comment = config_setting_add(user, "comment", CONFIG_TYPE_STRING); config_setting_set_string(comment, "test..."); /* ----------------- Remove ---------------- */ config_setting_remove(user, "code"); config_setting_remove_elem(tags, 1); /* ----------------- Set ------------------- */ config_setting_set_string(name, "Rainsoft"); config_setting_set_string_elem(data, 0, "Ubuntu"); /* ----------------- Write ----------------- */ config_write(conf, stdout); }
输出:
title = "Test Application"; version = 1; app : { user : { name = "Rainsoft"; tags = [ "t1", "t3" ]; data = ( "Ubuntu", 1234 ); comment = "test..."; }; };
4. Q&A
(1) 调用 config_destroy(conf) 后,其分配的字符串会被全部释放,因此得自己注意 strcpy / strdup。
(2) 官方文档中标明了 "Libconfig is not thread-safe","Libconfig is not async-safe" ……
(3) 似乎 Array/List 必须是 Group Member,不知道是不是版本的问题。