Retrouvez cet article dans : Linux Magazine 87
Introduction
Lib, bibliothèque d’utilitaires pour le programmeur en C, propose des outils pour vous faciliter la vie avec les chaînes de caractères. Il en existe de deux sortes. L’une d’elle consiste à reprendre et compléter les fonctions existantes. Ainsi, à la place deLe type GString
Voici ce que vous pouvez lire dans01 typedef struct _GString               GString;     Â
et
01 struct _GString
02 {
03  gchar *str;
04Â Â gsize len;Â Â Â
05Â Â gsize allocated_len;
06 };
Comprenez : ce type stocke votre chaîne de caractères dans son champ 
 Fig. 1 : Gstring
Créer une nouvelle chaîne de caractères
g_string_new()
Lorsque vous souhaitez créer une nouvelle chaîne de caractères, vous avez en général une chaîne, appelons-la " texte ", à y stocker. Voici le moyen le plus simple d’y arriver :
01 GString *c;
02 c = g_string_new("texte");
Voici ce que fait glib dans l’ombre de cette fonction :
01 GString*
02 g_string_new (const gchar *init)
03 {
04 GString *string;
05
06 if (init == NULL || *init == ‘\0’)
07 string = g_string_sized_new (2);
08 else
09 {
10 gint len;
11
12 len = strlen (init);
13 string = g_string_sized_new (len + 2);
14
15 g_string_append_len (string, init, len);
16 }
17
18 return string;
19 }
La lecture de ce code nous montre que cette fonction n’est rien d’autre qu’un appel à g_string_sized_new()
Cette fonction est le cœur de la création d’une nouvelle chaîne. Voici son contenu :01 GString*
02 g_string_sized_new (gsize dfl_size)Â Â Â
03 {
04Â Â GString *string = g_slice_new (GString);
05
06Â Â string->allocated_len = 0;
07  string->len  = 0;
08  string->str  = NULL;
09
10Â Â g_string_maybe_expand (string, MAX (dfl_size, 2));
11Â Â string->str[0] = 0;
12
13Â Â return string;
14 }
Le début est facile à comprendre : nous allouons un peu de mémoire pour le type g_string_maybe_expand()
Mais qu’est-ce donc bien que cette chose qui n’est rien d’autre qu’une fonction interne ?01 static void
02 g_string_maybe_expand (GString* string,
03 gsize len)
04 {
05 if (string->len + len >= string->allocated_len)
06 {
07 string->allocated_len = nearest_power (1, string->len + len + 1);
08 string->str = g_realloc (string->str, string->allocated_len);
09 }
10 }
Vous disposez d’une chaîne de caractères 01 static inline gsize
02 nearest_power (gsize base, gsize num)
03 {
04 if (num > MY_MAXSIZE / 2)
05 {
06 return MY_MAXSIZE;
07 }
08 else
09 {
10 gsize n = base;
11
12 while (n < num)
13 n <<= 1;
14
15 return n;
16 }
17 }
Contrairement au reste du code, celui-ci n’est pas très facile à lire. Pourtant, il est simple. Le cœur de cette fonction se trouve aux lignes 12 et 13. Tant que Supprimer une chaîne de caractères
La fonction 01 gchar*
02 g_string_free (GString *string,
03 gboolean free_segment)
04 {
05 gchar *segment;
06
07 g_return_val_if_fail (string != NULL, NULL);
08
09 if (free_segment)
10 {
11 g_free (string->str);
12 segment = NULL;
13 }
14 else
15 segment = string->str;
16
17 g_slice_free (GString, string);
18
19 return segment;
20 }
Cette fonction est plutôt simple à comprendre. Un premier test ligne 7 nous fait sortir si la chaîne de caractère n’est rien d’autre que Modifier la chaîne de caractères
Remplacer son contenu
Remplacer du contenu signifie initialiser le GString avec une nouvelle chaîne.01 GString*
02 g_string_assign (GString *string,
03 const gchar *rval)
04 {
05 g_return_val_if_fail (string != NULL, NULL);
06 g_return_val_if_fail (rval != NULL, string);
07
08 /* Make sure assigning to itself doesn’t corrupt the string. */
09 if (string->str != rval)
10 {
11 /* Assigning from substring should be ok since g_string_truncate
12 does not realloc. */
13 g_string_truncate (string, 0);
14 g_string_append (string, rval);
15 }
16
17 return string;
18 }
Après deux vérifications sur les paramètres (lignes 5 et 6), suivies d’une troisième vérification pour ne pas remplacer la chaîne par elle-même (ligne 8), nous utilisons des fonctions déjà existantes. La fonction 01 GString*
02 g_string_truncate (GString *string,
03 gsize len)
04 {
05 g_return_val_if_fail (string != NULL, NULL);
06
07 string->len = MIN (len, string->len);
08 string->str[string->len] = 0;
09
10 return string;
11 }
Comme la fonction précédente, celle-ci commence par un test d’argument (ligne 5). Puis la longueur de la chaîne est diminuée comme souhaité (ligne 7). Vous constatez que rien n’est fait si celle-ci était déjà plus petite. Enfin, un caractère nul est inséré à la position indiquée par la taille de la chaîne pour marquer la fin de celle-ci (ligne 8). CommeAjouter du contenu à la fin
C’est ici que nous allons voir le code de01 GString*
02 g_string_append (GString *string,
03 const gchar *val)
04 {
05 g_return_val_if_fail (string != NULL, NULL);
06 g_return_val_if_fail (val != NULL, string);
07
08 return g_string_insert_len (string, -1, val, -1);
09 }
Cette fonction n’est rien d’autre qu’un appel à la fonction 01 GString*
02 g_string_append_len (GString *string,
03 const gchar *val,
04 gssize len)
05 {
06 g_return_val_if_fail (string != NULL, NULL);
07 g_return_val_if_fail (val != NULL, string);
08
09 return g_string_insert_len (string, -1, val, len);
10 }
Voyons cette fameuse01 GString* 02 g_string_insert_len (GString *string, 03 gssize pos, 04 const gchar *val, 05 gssize len)Cette fonction insère dans la chaîne
06 {
07 g_return_val_if_fail (string != NULL, NULL);
08 g_return_val_if_fail (val != NULL, string);
09
10 if (len < 0)
11 len = strlen (val);
12
13 if (pos < 0)
14 pos = string->len;
15 else
16 g_return_val_if_fail (pos <= string->len, string);
17
Après les vérifications d’usage dans glib (lignes 7 et 8), il se peut que des arguments aient été placés à 18 /* Check whether val represents a substring of string. This test
19 probably violates chapter and verse of the C standards, since
20 ">=" and "<=" are only valid when val really is a substring.
21 In practice, it will work on modern archs. */
22 if (val >= string->str && val <= string->str + string->len)
23 {
La ligne 22 n’est effectivement pas conforme à la littérature sur le C : c’est mal de comparer des pointeurs ! Et vous, comment auriez-vous fait ? Le code suivant est découpé en trois parties :24 gsize offset = val - string->str; 25 gsize precount = 0; 26 27 g_string_maybe_expand (string, len); 28 val = string->str + offset; 29 /* At this point, val is valid again. */ 30 31 /* Open up space where we are going to insert. */ 32 if (pos < string->len) 33 g_memmove (string->str + pos + len, string->str + pos, string->len - pos); 34Maintenant que le trou est creusé, nous pouvons le remplir. Question : que se passe-t-il si nous avons découpé notre chaîne
35 /* Move the source part before the gap, if any. */
36 if (offset < pos)
37 {
38 precount = MIN (len, pos - offset);
39 memcpy (string->str + pos, val, precount);
40 }
41
Il ne reste plus qu’à faire de même pour la fin de la chaîne, si nous n’avons pas déjà tout copié (dans ce cas, 42 /* Move the source part after the gap, if any. */
43 if (len > precount)
44 memcpy (string->str + pos + precount,
45 val + /* Already moved: */ precount + /* Space opened up: */ len,
46 len - precount);
47 }
48 else
49 {
Dans cette deuxième partie, c’est beaucoup plus simple, car nous ne coupons pas la chaîne à insérer en deux. Il suffit donc d’étendre le 50 g_string_maybe_expand (string, len); 51 52 /* If we aren’t appending at the end, move a hunk 53 * of the old string to the end, opening up space 54 */ 55 if (pos < string->len) 56 g_memmove (string->str + pos + len, string->str + pos, string->len - pos); 57 58 /* insert the new string */ 59 if (len == 1) 60 string->str[pos] = *val; 61 else 62 memcpy (string->str + pos, val, len); 63 } 64Il ne reste plus qu’à faire un peu de maintenance, en modifiant la longueur du
65 string->len += len; 66 67 string->str[string->len] = 0; 68 69 return string; 70 }
Ajouter du contenu au début
Nous allons retrouver, via les deux fonctions suivantes, des appels Ã01 GString*
02 g_string_prepend (GString *string,
03 const gchar *val)
04 {
05 g_return_val_if_fail (string != NULL, NULL);
06 g_return_val_if_fail (val != NULL, string);
07
08 return g_string_insert_len (string, 0, val, -1);
09 }
01 GString*
02 g_string_prepend_len (GString *string,
03 const gchar *val,
04 gssize len)
05 {
06 g_return_val_if_fail (string != NULL, NULL);
07 g_return_val_if_fail (val != NULL, string);
08
09 return g_string_insert_len (string, 0, val, len);
10 }
g_string_printf()
Que serait une gestion des chaînes de caractères sans l’équivalent de01 void
02 g_string_printf (GString *string,
03 const gchar *fmt,
04 ...)
05 {
06 va_list args;
07
08 g_string_truncate (string, 0);
09
10 va_start (args, fmt);
11 g_string_append_printf_internal (string, fmt, args);
12 va_end (args);
13 }
Cette fonction, comme d’autres que nous avons pu voir, est frustrante : après avoir effacé l’ancienne chaîne (ligne 8), nous avons une simple gestion du nombre variable d’arguments (lignes 10 et 12) et un appel à 01 static void
02 g_string_append_printf_internal (GString *string,
03 const gchar *fmt,
04 va_list args)
05 {
06 gchar *buffer;
07 gint length;
08
09 length = g_vasprintf (&buffer, fmt, args);
10 g_string_append_len (string, buffer, length);
11 g_free (buffer);
12 }
Vous devinez que, ligne 9, 01 gint
02 g_vasprintf (gchar **string,
03 gchar const *format,
04 va_list args)
05 {
06 gint len;
07 g_return_val_if_fail (string != NULL, -1);
08
09 #if !defined(HAVE_GOOD_PRINTF)
10
11 len = _g_gnulib_vasprintf (string, format, args);
12 if (len < 0)
13 *string = NULL;
14
15 #elif defined (HAVE_VASPRINTF)
16
17 len = vasprintf (string, format, args);
18 if (len < 0)
19 *string = NULL;
20 else if (!g_mem_is_system_malloc ())
21 {
22 /* vasprintf returns malloc-allocated memory */
23 gchar *string1 = g_strndup (*string, len);
24 free (*string);
25 *string = string1;
26 }
27
28 #else
29
30 {
31 va_list args2;
32
33 G_VA_COPY (args2, args);
34
35 *string = g_new (gchar, g_printf_string_upper_bound (format, args));
36
37 len = _g_vsprintf (*string, format, args2);
38 va_end (args2);
39 }
40 #endif
41
42 return len;
43 }
Cette fonction contient en réalité trois contenus différents.
- Soit vous disposez de
_g_gnulib_vasprintf(). Dans ce cas, comme le code vous le montre lignes 11 à 13, cette fonction fait tout le travail. Il n’y a pas de raison de s’en priver. - Soit vous disposez de
vasprintf(). Cette fonction effectue également tout le travail. Cependant, par souci de compatibilité avec glib, l’allocation de mémoire doit se faire avec une fonction de la famille deg_malloc()et non demalloc(). C’est pour cela que, lignes 23 à 25, la chaîne est dupliquée avecg_strndup()et libérée avecfree(). C’est le duplicata qui sera renvoyé. - Soit vous ne disposez de rien du tout, et c’est là que nous allons apprendre à faire cela nous-même.
01 gsize
02 g_printf_string_upper_bound (const gchar *format,
03 va_list args)
04 {
05 gchar c;
06 return _g_vsnprintf (&c, 1, format, args) + 1;
07 }
Sur notre machine, la fonction 01 #define _g_vsnprintf _g_gnulib_vsnprintfNous finissons le jeu de piste dans le fichier
01 int _g_gnulib_vsnprintf (char *string, size_t n, char const *format, va_list args)
02 {
03 char *result;
04 size_t length;
05
06 result = vasnprintf (NULL, &length, format, args);
07 if (result == NULL)
08 return -1;
09
10 if (n > 0)
11 {
12 memcpy (string, result, MIN(length + 1, n));
13 string[n - 1] = 0;
14 }
15
16 free (result);
17
18 return length;
19 }
La fonction Passer une chaîne de caractères en majuscules
Nous allons finir cet article avec une fonction qui va nous entraîner dans un nouveau jeu de piste, bien plus amusant que les précédents. En effet, son code est simple, mais fait appel à de nombreux éléments de glib. En voici le contenu :g_string_ascii_up()
 01 GString*
02 g_string_ascii_up (GString *string)
03 {
04 gchar *s;
05 gint n;
06
07 g_return_val_if_fail (string != NULL, NULL);
08
09 n = string->len;
10 s = string->str;
11
12 while (n)
13 {
14 *s = g_ascii_toupper (*s);
15 s++;
16 n--;
17 }
18
19 return string;
20 }
Il s’agit de mettre une chaîne g_ascii_toupper()
Il se trouve dans01 gchar
02 g_ascii_toupper (gchar c)
03 {
04 return g_ascii_islower (c) ? c - ‘a’ + ‘A’ : c;
05 }
Facile à comprendre : le caractère est-il en minuscules (g_ascii_islower()
Avez-vous trouvé le code de cette fonction ? Il est dans01 #define g_ascii_islower(c) \ 02 ((g_ascii_table[(guchar) (c)] & G_ASCII_LOWER) != 0)Le jeu de piste continue avec
GAsciiType
Un peu plus haut dans le fichier01 typedef enum {
02 G_ASCII_ALNUM = 1 << 0,
03 G_ASCII_ALPHA = 1 << 1,
04 G_ASCII_CNTRL = 1 << 2,
05 G_ASCII_DIGIT = 1 << 3,
06 G_ASCII_GRAPH = 1 << 4,
07 G_ASCII_LOWER = 1 << 5,
08 G_ASCII_PRINT = 1 << 6,
09 G_ASCII_PUNCT = 1 << 7,
10 G_ASCII_SPACE = 1 << 8,
11 G_ASCII_UPPER = 1 << 9,
12 G_ASCII_XDIGIT = 1 << 10
13 } GAsciiType;
Nous avons donc bien un champ de bits, où nous retrouvons ligne 7 celui que nous cherchions. Quant au tableau 01 GLIB_VAR const guint16 * const g_ascii_table;Cette déclaration du tableau ne nous en apprend pas tant que ça. Le jeu de piste n’est pas fini !
g_ascii_table
C’est dans01 static const guint16 ascii_table_data[256] = {
02 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
03 0x004, 0x104, 0x104, 0x004, 0x104, 0x104, 0x004, 0x004,
04 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
05 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004,
06 0x140, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
07 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
08 0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459, 0x459,
09 0x459, 0x459, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
10 0x0d0, 0x653, 0x653, 0x653, 0x653, 0x653, 0x653, 0x253,
11 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
12 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253, 0x253,
13 0x253, 0x253, 0x253, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x0d0,
14 0x0d0, 0x473, 0x473, 0x473, 0x473, 0x473, 0x473, 0x073,
15 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
16 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073, 0x073,
17 0x073, 0x073, 0x073, 0x0d0, 0x0d0, 0x0d0, 0x0d0, 0x004
18 /* the upper 128 are all zeroes */
19 };
20
21 const guint16 * const g_ascii_table = ascii_table_data;
Il est aussi illisible que compréhensible : nous avons bien des champs de bits lignes 1 à 19. La ligne 21 fait pointer g_ascii_table sur le tableau ascii_table_data[].
guchar
Dans notre jeu de piste, il ne manque plus que le mot clé 01 typedef unsigned char guchar;
Conclusion
Le jeu de piste est fini. En guise de conclusion, voyons les quelques manières de gagner dans un jeu de piste comme le précédent. Votre premier outil n’est rien d’autre que la commande$ grep g_ascii_table * glib.symbols:g_ascii_table gstrfuncs.c:const guint16 * const g_ascii_table = ascii_table_data; gstrfuncs.h:GLIB_VAR const guint16 * const g_ascii_table; gstrfuncs.h: ((g_ascii_table[(guchar) (c)] & G_ASCII_ALNUM) != 0) gstrfuncs.h: ((g_ascii_table[(guchar) (c)] & G_ASCII_ALPHA) != 0) gstrfuncs.h: ((g_ascii_table[(guchar) (c)] & G_ASCII_CNTRL) != 0) gstrfuncs.h: ((g_ascii_table[(guchar) (c)] & G_ASCII_DIGIT) != 0) gstrfuncs.h: ((g_ascii_table[(guchar) (c)] & G_ASCII_GRAPH) != 0) gstrfuncs.h: ((g_ascii_table[(guchar) (c)] & G_ASCII_LOWER) != 0) gstrfuncs.h: ((g_ascii_table[(guchar) (c)] & G_ASCII_PRINT) != 0) gstrfuncs.h: ((g_ascii_table[(guchar) (c)] & G_ASCII_PUNCT) != 0) gstrfuncs.h: ((g_ascii_table[(guchar) (c)] & G_ASCII_SPACE) != 0) gstrfuncs.h: ((g_ascii_table[(guchar) (c)] & G_ASCII_UPPER) != 0) gstrfuncs.h: ((g_ascii_table[(guchar) (c)] & G_ASCII_XDIGIT) != 0)Cette recherche venant de ce que nous lisions dans
/glib-2.12.2/glib$ find . -type f -exec grep vasnprintf {} \; -print
[...]
#define vasnprintf _g_gnulib_vasnprintf
./gnulib/g-gnulib.h
extern char * vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args)
./gnulib/vasnprintf.h
#include "vasnprintf.h"
vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args)
./gnulib/vasnprintf.c
[...]
Le résultat est assez explicite dès que vous avez noté que le nom du fichier apparaît après la ou les lignes contenant le motif recherché. Vous pouvez donc, ici, chercher dans - Le site de GTK+ : http://www.gtk.org/
- C en action, O’Reilly, chapitre 8.
Retrouvez cet article dans : Linux Magazine 87





Donnez votre avis
Vous devez avoir ouvert une session pour écrire un commentaire.