本文主要参考:
glib-Key-value-file-parser
本节主要讲解Key-value文件解析器,可以轻松的帮我们实现配置文件的解析,类似于.ini文件的解析。
1 头文件
#include <glib.h>
#include <glib/gprintf.h>
2 描述
GKeyFile允许您解析、编辑或创建包含键值对组的文件,因为缺少更好的名称,我们将其称为“key files”。 现在,一些freedesktop.org规范使用key files,例如 Desktop Entry Specification 和 Icon Theme Specification.。
Desktop Entry Specification中详细描述了key files的语法,这里有一个快速摘要:key files由键值对组成,其中包含注释。
# this is just an example
# there can be comments before the first group
[First Group]
Name=Key File Example\tthis value shows\nescaping
# localized strings are stored in multiple key-value pairs
Welcome=Hello
Welcome[de]=Hallo
Welcome[fr_FR]=Bonjour
Welcome[it]=Ciao
Welcome[be@latin]=Hello
[Another Group]
Numbers=2;20;-200;0
Booleans=true;false;true;true
以“#”开头的行和空白行被视为注释。
组由标题行启动,组名称被包含在“[‘和’]'中,并在下一组的开头或文件末尾隐式结束。 每个键值对必须包含在一个组中。
键值对通常具有key = value
形式,但本地化字符串除外,其形式为key[locale]=value
,其区域设置标识符为lang_COUNTRY@MODIFIER
,其中COUNTRY
和MODIFIER
是可选的。 “=
”字符前后的空格将被忽略。 值中的换行符、制表符、回车符和反斜杠字符分别转义为\ n
,\ t
,\ r
和\\
。 要保留值中的前导空格,也可以将它们转义为\ s
。
Key files 可以存储字符串(可能包含本地化变体)、整数、布尔值和这些列表。 列表由分隔符分隔,通常为“;
” 要么 ‘,
’。 要在列表中的值中使用列表分隔符,必须通过在其前面添加反斜杠来对其进行转义。
这种语法显然受到Windows上常见的.ini文件的启发,但有一些重要的区别:
-
.ini文件使用’
;
‘字符开始注释,key files使用’#
'字符 -
Key files不允许未分组的keys ,这意味着只有注释可以在第一组之前
-
Key files 始终以UTF-8编码
-
Key 和组名称区分大小写。 例如,名为[GROUP]的组与[group]不同。
-
.ini 文件没有强类型的布尔条目类型,它们只有GetProfileInt()。 在key files中,只允许使用true和false(小写)
请注意,与Desktop Entry Specification相反,key files中的组可能多次包含相同的key:最后一个条目获胜。 Key files也可能包含多个具有相同名称的组:他们合并在一起。 另一个区别是key files中的 key 和组名称不限于ASCII字符。
以下是加载key file和读取值的示例:
g_autoptr(GError) error = NULL;
g_autoptr(GKeyFile) key_file = g_key_file_new ();
if (!g_key_file_load_from_file (key_file, "key-file.ini", flags, &error))
{
if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
g_warning ("Error loading key file: %s", error->message);
return;
}
g_autofree gchar *val = g_key_file_get_string (key_file, "Group Name", "SomeKey", &error);
if (val == NULL &&
!g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
{
g_warning ("Error finding key in key file: %s", error->message);
return;
}
else if (val == NULL)
{
// Fall back to a default value.
val = g_strdup ("default-value");
}
以下是创建和保存key file的示例:
g_autoptr(GKeyFile) key_file = g_key_file_new ();
const gchar *val = …;
g_autoptr(GError) error = NULL;
g_key_file_set_string (key_file, "Group Name", "SomeKey", val);
// Save as a file.
if (!g_key_file_save_to_file (key_file, "key-file.ini", &error))
{
g_warning ("Error saving key file: %s", error->message);
return;
}
// Or store to a GBytes for use elsewhere.
gsize data_len;
g_autofree guint8 *data = (guint8 *) g_key_file_to_data (key_file, &data_len, &error);
if (data == NULL)
{
g_warning ("Error saving key file: %s", error->message);
return;
}
g_autoptr(GBytes) bytes = g_bytes_new_take (g_steal_pointer (&data), data_len);
3 API
https://developer.gnome.org/glib/2.60/glib-Key-value-file-parser.html#g-key-file-new
4 实例
以下示例是基于之前写的一篇博文的基础上扩展的,详见:
【glib】标准化程序的命令行解析 GOptionEntry
4.1 本文Key-value文件解析器如下:
// test.c
#include <glib.h>
#include <glib/gprintf.h>
#include <locale.h>
#define CONFIG_GROUP_SOURCE "display"
#define CONFIG_GROUP_SOURCE1 "source1"
#define CONFIG_DISPLAY_WIDTH "width"
#define CONFIG_SOURCE1_ENABLE "enable"
#define CONFIG_SOURCE1_TYPE "type"
#define CONFIG_SOURCE1_NUM_SOURCES "num-sources"
#define CONFIG_SOURCE1_GPU_ID "gpu-id"
#define CONFIG_SOURCE1_CUDADEC_MEMTYPE "cudadec-memtype"
#define ERR_MSG_V(msg, ...) \
g_print("** ERROR: <%s:%d>: " msg "\n", __func__, __LINE__, ##__VA_ARGS__)
typedef struct
{
int enable;
int type;
int num_sources;
int gpu_id;
int cudadec_memtype;
}src1;
static gboolean beep = FALSE;
static gchar **cfg_files = NULL;
static guint num_instances;
static GOptionEntry entries[] =
{
{"beep" , 'b' , 0, G_OPTION_ARG_NONE, &beep, "Beep when done" , NULL},
{"cfg-file", 'c', 0, G_OPTION_ARG_FILENAME_ARRAY, &cfg_files, "Set the config file", NULL},
{NULL}
};
gboolean parse_source (GKeyFile *key_file, gchar *group)
{
gboolean ret = FALSE;
gchar **keys = NULL;
gchar **key = NULL;
GError *error = NULL;
guint rett;
keys = g_key_file_get_keys (key_file, group, NULL, &error);
for (key = keys; *key; key++)
{
rett = g_key_file_get_integer (key_file, group, *key, &error);
g_print("\t %s = %d\n", *key, rett);
}
ret = TRUE;
return ret;
}
gboolean update_source (GKeyFile *key_file, gchar *group)
{
gboolean ret = FALSE;
gchar **keys = NULL;
gchar **key = NULL;
GError *error = NULL;
guint rett;
keys = g_key_file_get_keys (key_file, group, NULL, &error);
for (key = keys; *key; key++)
{
if (!strncmp (*key, CONFIG_DISPLAY_WIDTH, sizeof (CONFIG_DISPLAY_WIDTH) - 1))
g_key_file_set_value (key_file, group, *key, "1920");
}
ret = TRUE;
return ret;
}
gboolean add_source (GKeyFile *key_file, gchar *group, src1 src)
{
g_key_file_set_integer (key_file, CONFIG_GROUP_SOURCE1, CONFIG_SOURCE1_ENABLE, src.enable);
g_key_file_set_integer (key_file, CONFIG_GROUP_SOURCE1, CONFIG_SOURCE1_TYPE, src.type);
g_key_file_set_integer (key_file, CONFIG_GROUP_SOURCE1, CONFIG_SOURCE1_NUM_SOURCES, src.num_sources);
g_key_file_set_integer (key_file, CONFIG_GROUP_SOURCE1, CONFIG_SOURCE1_GPU_ID, src.gpu_id);
g_key_file_set_integer (key_file, CONFIG_GROUP_SOURCE1, CONFIG_SOURCE1_CUDADEC_MEMTYPE, src.cudadec_memtype);
return TRUE;
}
gboolean parse_config_file (gchar *cfg_file_path)
{
GKeyFile *cfg_file = g_key_file_new ();
GError *error = NULL;
gboolean ret = FALSE;
gchar **groups = NULL;
gchar **group;
guint j=0;
if (!g_key_file_load_from_file (cfg_file, cfg_file_path, G_KEY_FILE_NONE,
&error)) {
ERR_MSG_V("%s", error->message);
}
groups = g_key_file_get_groups (cfg_file, NULL);
for (group = groups; *group; group++)
{
g_print("group[%d] = %s\n", j++, *group);
if (!strncmp (*group, CONFIG_GROUP_SOURCE, sizeof (CONFIG_GROUP_SOURCE) - 1))
{
// 解析某一组
parse_source (cfg_file, *group);
}
}
g_key_file_free (cfg_file);
ret = TRUE;
return ret;
}
gboolean update_config_file (gchar *cfg_file_path, src1 src)
{
GKeyFile *cfg_file = g_key_file_new ();
GError *error = NULL;
gboolean ret = FALSE;
gchar **groups = NULL;
gchar **group;
guint j=0;
if (!g_key_file_load_from_file (cfg_file, cfg_file_path, G_KEY_FILE_NONE,
&error)) {
ERR_MSG_V("%s", error->message);
}
groups = g_key_file_get_groups (cfg_file, NULL);
for (group = groups; *group; group++)
{
//g_print("group[%d] = %s\n", j++, *group);
// 是否存在某个分组
if (!strncmp (*group, CONFIG_GROUP_SOURCE, sizeof (CONFIG_GROUP_SOURCE) - 1))
{
// 解析某一组
update_source (cfg_file, *group);
}
if (strncmp (*group, CONFIG_GROUP_SOURCE1, sizeof (CONFIG_GROUP_SOURCE1) - 1))
{
// 解析某一组
add_source (cfg_file, *group, src);
}
}
if (!g_key_file_save_to_file (cfg_file, cfg_file_path, &error))
{
g_warning ("Error saving key file: %s", error->message);
return;
}
g_key_file_free (cfg_file);
ret = TRUE;
return ret;
}
int main (int argc, char *argv[])
{
GError *error = NULL;
GOptionContext *context = NULL;
GOptionGroup *group = NULL;
guint i;
src1 src ={1,2,1,0,0};
// 创建一个新的选项上下文
context = g_option_context_new("- test tree model performance" );
// 如果主要组不存在则创建主要组,向组添加entries并设置转换域
g_option_context_add_main_entries(context, entries, NULL);
//添加要在选项列表之前的--help输出中显示的字符串。 这通常是程序功能的摘要
g_option_context_set_summary(context, "This is a glib demo" );
// 解析命令行参数,识别已添加到上下文的选项
if (!g_option_context_parse(context, &argc, &argv, &error))
{
ERR_MSG_V("%s", error->message);
exit (1);
}
if (cfg_files)
{
num_instances = g_strv_length (cfg_files);
}
for (i = 0; i < num_instances; i++)
{
//g_print("\n\ncfg_files[%d]=%s\n",i,cfg_files[i]);
parse_config_file (cfg_files[i]);
update_config_file (cfg_files[i], src);
}
// 释放被解析的参数
g_option_context_free(context);
return 0;
}
4.2 在准备一个Key-value文件示例文件 tt.txt
# This is test for key-value on glib
[application]
enable-perf-measurement=1
perf-measurement-interval-sec=5
#gie-kitti-output-dir=streamscl
[display]
enable=1
rows=2
columns=2
width=1280
height=720
gpu-id=0
nvbuf-memory-type=0
[source0]
enable=1
type=3
num-sources=4
gpu-id=0
cudadec-memtype=0
[sink]
enable=0
type=3
codec=1
sync=0
bitrate=2000000
output-file=out.mp4
source-id=0
4.3 编译:
gcc test.c -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include \
-L/usr/lib/x86_64-linux-gnu -lglib-2.0 -o test
4.4 运行:
./test -c tt.txt
4.5 运行结果输出如下:
tt.txt配置文件更新后的效果:
注意:示例代码中,只打印了组为“display”的键值对。
项目代码下载:https://download.csdn.net/download/u013554213/11439139