Key File Structure

Key files in GLib are much like .ini files used on Microsoft Windows machines. Data is split into groups, where the name appears between square bracket characters, and comments are started by the pound character.

Key files, parsed by GKeyFile, keep track of strings, localized strings, Boolean values, integers, doubles, and lists of each of these data types. An example key file can be viewed below in Listing 1.

Listing 1Key-value file (keyfile.conf)

# This is an example key-value file

[username]

Name=Foundations of GTK+ Development

# A list of strings that are localized
Hi=Hello
Hi[fr]=Bonjour
Hi[de]=Hallo
Hi[es]=Hola

Bool=true;true;false
Nums=1;1;2;3;5;8
Strings=One;One;Two;Three;Five;Eight
Int=42
Doubles=0.0;1.0;50.4

Key files are very simple to use and are human-readable, so they are ideal for many types of configurations. Another option is to use GLib's simple XML parser, which is covered in Chapter 13 of Foundations of GTK+ Development.

Creating a New Key File

New GKeyFile objects are created with g_key_file_new(). This function creates an empty object. From there, you can either add groups and data manually or you can load data from a file. There will be more on that later.

GKeyFile* g_key_file_new ();

When you add or read lists of data, one character is used as a separator. Usually this is the semicolon character, which means that it must always be prefixed by a back slash if you want that character to actually appear in a list. You can use g_key_file_set_list_separator to change the character that is used to separate lists.

void g_key_file_set_list_separator (GKeyFile *file,
                                    gchar separator);

When you are done editing a GKeyFile object, you will usually want to save the file. You can useg_key_file_to_data() to convert the data held in the object into a string. This string will be allocated for you, so you should free it will g_free() when it is no longer needed.

gchar* g_key_file_to_data (GKeyFile *file,
                           gsize *length,
                           GError *error);

When you are done with a GKeyFile object, you should free it with g_key_file_free(). This will clean up any allocated resources that are no longer needed.

void g_key_file_free (GKeyFile *file);

Setting and Retrieving Data

As previously stated, data is held in groups. The g_key_file_get_groups() function returns a NULL-terminated list of strings, each corresponding to a group. The function also returns the length of the array.

gchar** g_key_file_get_groups (GKeyFile *file,
                               gsize *length);

In order to access a piece of data, you must first specify which group it belongs to. This allows each group to contain the same types. Table 1, located directly below, gives a complete list of functions that can be used to access data in a GKeyFile object. When you find the function you need, you should reference the GLib API documentation for more information about function parameters and the return value. Each of the functions found in Table 1 also has a correspondingg_key_file_get_*() function.

Removing Entries

In addition to the ability to add, edit, and retrieve information from a key file, it is also useful to be able to remove elements. The following three functions are used for removing information from a key file. The g_key_file_remove_group() function will remove the specified group from the file if it exists.

void g_key_file_remove_group (GKeyFile *file,
                              const gchar *group,
                              GError **error);

You can also remove a specific key with g_key_file_remove_key(). You should specify a group that the key will be removed from.

void g_key_file_remove_key (GKeyFile *file,
                            const gchar *group,
                            const gchar *key,
                            GError **error);

Lastly, you can use g_key_file_remove_comment() to remove a comment from the key file. If neither the group or key are specified, the comment at the top of the key file will be removed. If you specify a group, but not a key, the comment above the group will be removed. Specifying both the key and the group will remove the comment that is located above that specific key.

void g_key_file_remove_comment (GKeyFile *file,
                                const gchar *group,
                                const gchar *key,
                                GError **error);

Parsing an Existing Key File

Parsing a key file that already exists is a basic requirement for a utility such as this, so GLib provides g_key_file_load_from_file(). This function will parse the key file found at the given location, and returns TRUE if it was successfully parsed.

gboolean g_key_file_load_from_file (GKeyFile *file,
                                    const gchar *fn,
                                    GKeyFileFlags flags,
                                    GError *error);

The third parameter of this function is a bitwise list of GKeyFileFlags. The three values provided by this enumeration follow:

  • G_KEY_FILE_NONE: No flags for the key file will be set.

  • G_KEY_FILE_KEEP_COMMENTS: All of the comments in the key file should be read so that they can remain in the same position when the file is saved. If you do not set this flag, all comments will be lost.

  • G_KEY_FILE_KEEP_TRANSLATIONS: All of the translations in the key file should be read so that they will not be lost when you save the file.

If you have already read the content of the key file, you can parse a string withg_key_file_load_from_data(). You should specify the length of the string as the third parameter of this function.

gboolean g_key_file_load_from_data (GKeyFile *file,
                                    const gchar *data,
                                    gsize length,
                                    GKeyFileFlags flags,
                                    GError *error);

A Simple Example

typedef struct
{
  gchar *name, *hello;
  gboolean *boolean;
  int *nums;
  gchar **strings;
  int meaning_of_life;
  gdouble *doubles;
} Settings;

int main ()
{
  Settings *conf;
  GKeyFile *keyfile;
  GKeyFileFlags flags;
  GError *error = NULL;
  gsize length;
  
  /* Create a new GKeyFile object and a bitwise list of flags. */
  keyfile = g_key_file_new ();
  flags = G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS;
  
  /* Load the GKeyFile from keyfile.conf or return. */
  if (!g_key_file_load_from_file (keyfile, "keyfile.conf", flags, &error))
  {
      g_error (error->message);
      return -1;
  }
  
  /* Create a new Settings object. If you are using GTK+ 2.8 or below, you should
   * use g_new() or g_malloc() instead! */
  conf = g_slice_new (Settings);
  
  /* Read in data from the key file from the group "username". */
  conf->name = g_key_file_get_string(keyfile, "username","Name", NULL);
  conf->hello = g_key_file_get_locale_string(keyfile, "username","Hi", "es", NULL);
  conf->boolean = g_key_file_get_boolean_list(keyfile, "username","Bool", &length, NULL);
  conf->nums = g_key_file_get_integer_list(keyfile, "username","Nums", &length, NULL);
  conf->strings = g_key_file_get_string_list(keyfile, "username","Strings", &length, NULL);
  conf->meaning_of_life = g_key_file_get_integer(keyfile, "username","Int", NULL);
  conf->doubles = g_key_file_get_double_list(keyfile, "username","Doubles", &length, NULL);
  g_key_file_free(keyfile).
  return 0;
}



#include <glib.h>
#include <glib/gprintf.h>
char *get_value(const char *config_file, const char *group, const char *key);

char** getKeys(const char *config_file, const char *group,gsize *num_keys);

char** getGroups(const char * config_file,gsize * num_groups);

int hasKey(const char *config_file, const char *group, const char *key);

int hasGroup(const char *config_file, const char *group);

int add_keyvalue(
const char *config_file, const char *group, const char *key, const char *value);

int del_keyvalue(const char *config_file, const char *group, const char *key);

int del_group(const char *config_file, const char *group);


#include <stdio.h> 
#include <stdlib.h> 
#include "gkey_file.h" 
/**
 * get value by key
 */
char *get_value(const char *config_file, const char *group, const char *key) 
{
    GKeyFile *gkf = NULL;
    gkf = g_key_file_new();
    if (!g_key_file_load_from_file(gkf, config_file, G_KEY_FILE_NONE, NULL)) {
      fprintf(stderr, "get_value,could not read config file %s\n", config_file);
    }
    gchar *value = g_key_file_get_value(gkf, group, key, NULL);

    g_key_file_free(gkf);
    return value;
}
/**
 * get all keys by group
 */
char** getKeys(const char *config_file, const char *group,gsize *num_keys){
    GKeyFile *gkf = NULL;
    gkf = g_key_file_new();
    if (!g_key_file_load_from_file(gkf, config_file, G_KEY_FILE_NONE, NULL)) {
        fprintf(stderr, "getKeys,could not read config file %s\n", config_file);
    }
    gchar **keys = g_key_file_get_keys(gkf, group, num_keys, NULL);
    g_key_file_free(gkf);

    return keys;
}
/**
 * get all groups
 */
char** getGroups(const char *config_file,gsize *num_groups){
    GKeyFile *gkf = NULL;
    gkf = g_key_file_new();
    if (!g_key_file_load_from_file(gkf, config_file, G_KEY_FILE_NONE, NULL)) {
        fprintf(stderr, "getKeys,could not read config file %s\n", config_file);
    }
    gchar **groups = g_key_file_get_groups(gkf,num_groups);
    g_key_file_free(gkf);

    return groups;
}

/**
 * is exist key by group
 */
int hasKey(const char *config_file, const char *group, const char *key){
    GKeyFile *gkf = NULL;
    gkf = g_key_file_new();
    if (!g_key_file_load_from_file(gkf, config_file, G_KEY_FILE_NONE, NULL)) {
        fprintf(stderr, "hasKey,could not read config file %s\n", config_file);
    }
    gboolean value = g_key_file_has_key(gkf, group, key, NULL);
    
    g_key_file_free(gkf);
    return value;
}
/**
 * is exist group
 */
int hasGroup(const char *config_file, const char *group){
    GKeyFile *gkf = NULL;
    gkf = g_key_file_new();
    if (!g_key_file_load_from_file(gkf, config_file, G_KEY_FILE_NONE, NULL)) {
        fprintf(stderr, "hasKey,could not read config file %s\n", config_file);
    }
    gboolean value = g_key_file_has_group(gkf, group);
    
    g_key_file_free(gkf);
    return value;
}

/**
 * add key,value
 */
int add_keyvalue(const char *config_file, const char *group, const char *key, const char *value)
{
    int result = 1;
    GError  *p_error = NULL;
    gsize   size;
    gchar    *s_data;
    GKeyFile *gkf = NULL;
    gboolean    b_ret;
    gkf = g_key_file_new();
    if (!g_key_file_load_from_file(gkf, config_file, G_KEY_FILE_NONE, NULL)) {
        result =  0;
    }
    
    g_key_file_set_string(gkf,group,key,value);
    
    /* ----- Convert data to string */
    s_data = g_key_file_to_data(gkf, &size, &p_error);
    
    if (!s_data)
    {
       result =  0;
       fprintf(stderr, "%s add key[%s] error\n", config_file,key);
    }
    
    /* ----- Write data to file */
     b_ret = g_file_set_contents(config_file, s_data, size,  &p_error);
     g_free (s_data);
   
     if (!b_ret)
     {
      result =  0;
      fprintf(stderr, "%s add key[%s] error\n", config_file,key);
     }
   
     /* ----- Free GKeyFile */
     g_key_file_free(gkf);
     return result;
}
/**
 * delete key,value
 */
int del_keyvalue(const char *config_file, const char *group, const char *key){
    GError  *p_error = NULL;
    gsize   size;
    gchar    *s_data;
    GKeyFile *gkf = NULL;
    gboolean    b_ret;
    gkf = g_key_file_new();
    if (!g_key_file_load_from_file(gkf, config_file, G_KEY_FILE_NONE, NULL)) {
        fprintf(stderr, "del_keyvalue,could not read config file %s\n", config_file);
    }
    g_key_file_remove_key(gkf,group,key,NULL);

    gboolean value = !g_key_file_has_key(gkf,group,key,NULL);
    
    /* ----- Convert data to string */
    s_data = g_key_file_to_data(gkf, &size, &p_error);
    
    if (!s_data)
    {
       fprintf(stderr, "%s delete key[%s] error\n", config_file,key);
    }
    
    /* ----- Write data to file */
     b_ret = g_file_set_contents(config_file, s_data, size,  &p_error);
     g_free (s_data);

     if (!b_ret)
     {
      fprintf(stderr, "%s delete key[%s] error\n", config_file,key);
     }
    
    g_key_file_free(gkf);
    return value;
}
/**
 * delete group
 */
int del_group(const char *config_file, const char *group){
    GError  *p_error = NULL;
    gsize   size;
    gchar    *s_data;
    GKeyFile *gkf = NULL;
    gboolean    b_ret;
    gkf = g_key_file_new();
    if (!g_key_file_load_from_file(gkf, config_file, G_KEY_FILE_NONE, NULL)) {
        fprintf(stderr, "del_group,could not read config file %s\n", config_file);
    }
    g_key_file_remove_group(gkf,group,NULL);

    gboolean value = !g_key_file_has_group(gkf,group);    

    /* ----- Convert data to string */
    s_data = g_key_file_to_data(gkf, &size, &p_error);
    
    if (!s_data)
    {
       fprintf(stderr, "%s delete group[%s] error\n", config_file,group);
    }
    
    /* ----- Write data to file */
     b_ret = g_file_set_contents(config_file, s_data, size,  &p_error);
     g_free (s_data);

     if (!b_ret)
     {
      fprintf(stderr, "%s delete group[%s] error\n", config_file,group);
     }
    g_key_file_free(gkf);
    return value;
}
Note:
1、g_key_file_new and g_key_file_load_from_file is unsafe for multithreading environment.