【glib】Key-value文件解析器


glib源码
glib帮助文档

本文主要参考:
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 SpecificationIcon 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,其中COUNTRYMODIFIER是可选的。 “=”字符前后的空格将被忽略。 值中的换行符、制表符、回车符和反斜杠字符分别转义为\ 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

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页