Extraction d'informations depuis un fichier de données en C
Date de publication : 21/11/2009
IV. Traitement d'un fichier de type INI
IV-1. Etude algorithmique
IV-2. Implémentation
IV-2.a. Fonction : get
IV-2.b. Fonction : get_string
IV-2.c. Fonction : get_integer
IV-3. Code source complet
IV. Traitement d'un fichier de type INI
Dans ce chapitre nous allons étudier un format plus commun, un fichier de type *.ini comme ceux qu'on trouve sur le système
Windows ou encore dans le même esprit, les fichier *.conf qu'on peut trouver sur les Unixoïdes. Ce type de fichier est le
plus souvent utilisé pour stocker les préférences utilisateurs d'un programme. Voici l'exemple sur lequel
nous allons travailler :
[user]
alias=Franck.H
name=HECHT Franck
[server]
server=192.160.1.10
port=147
[files]
backup=backup.dat
log=log.dat
error=error.dat
|
IV-1. Etude algorithmique
Comme le chapitre précédent et tout code digne de ce nom, il faut établir un algorithme au moins minimaliste avec
les étapes principales qui serviront de fil rouge au code qu'on produira par la suite :
Ouvrir fichier
Tant que ligne <> NIL lire ligne fichier
Si section non ouverte et caractère 0 de ligne égal à '['
Test de la chaîne entre crochets
Si section égal ok
Ouvrir section
Fsi
Sinon si section ouverte
Si caractère 0 de ligne égal à '['
Fermeture section
Sortie de la boucle
Fsi
Recherche de la clé
Si clé trouvée
Récupération donnée
Sortie de la boucle
Fsi
Fsi
Fin tant que
Fermer fichier
|
Le principe reste assez simple ! Nous lisons le fichier ligne par ligne, il faut tester le premier caractère de la ligne
courante si la section n'a pas encore été ouverte/trouvée, ceci permet de détecter une section. Si la section a été trouvée
et ouverte, on commence par tester la même condition qu'au départ dans le cas où on se trouverais en dehors de la section
où il faut chercher les informations. Nous recherchons ensuite simplement la clé puis on en extrait la donnée.
IV-2. Implémentation
Nous allons découper le code en plusieurs fonctions comme dans le chapitre précédent, une fonction nommée get
qui servira à lire un enregistrement et qui ne sera pas visible directement par l'utilisateur et pour notre exemple,
une fonction get_string (qui va récupérer une chaîne) et get_integer (qui servira à récupérer une valeur entière).
Voici le nécessaire pour commencer (ajoutez également les deux fonctions fournies dans les pré-requis) :
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define FILENAME " conf . ini "
# define BUF_SIZE 128
# define BEGIN ' [ '
# define SEP ' = '
|
Ceci est le début du code soit les fichiers d'en-tête ainsi que quelques constantes symboliques nécessaires à la suite
du code. On précise ici tout de suite le nom du fichier par la constante FILENAME, on ajoute à celà la constante
BEGIN qui symbolise le début d'une section et le séparateur par la constante SEP.
IV-2.a. Fonction : get
char * get (const char * filename, const char * section, const char * key)
{
char * ret = NULL ;
char buff [BUF_SIZE];
FILE * file = NULL ;
int opened = 0 ;
file = fopen (filename, " r " );
if (file ! = NULL )
{
while ((fgets (buff, BUF_SIZE, file)) ! = NULL )
{
str_finalize (buff);
if (! opened & & buff [0 ] = = BEGIN)
{
char * p = buff;
if (memcmp (p + 1 , section, strlen (buff) - 2 ) = = 0 )
{
opened = 1 ;
continue ;
}
}
else if (opened)
{
if (buff [0 ] = = BEGIN)
{
opened = 0 ;
break ;
}
if (strstr (buff, key) ! = NULL )
{
char * p = strchr (buff, SEP);
if (p ! = NULL )
{
p+ + ;
ret = str_dup (p);
break ;
}
}
}
}
fclose (file);
}
return ret;
}
|
Cette fonction prend trois arguments
- Le nom du fichier INI
- Le nom de la section
- Le nom de la clé d'enregistrement
Comme d'habitude, on ouvre le fichier passé en argument
(1), on lit le fichier ligne par ligne avec une
boucle
while et avec la fonction
fgets. Avant toutes autres opérations sur la chaîne récupérée,
on remplace le saut de ligne qui est automatiquement lu et ajouté par la fonction fgets, par un caractère de fin de chaîne.
On teste si la section n'est pas déjà ouverte et en même temps, si le premier caractère est
'['
(3). Si la condition est vraie, on compare la chaîne (en l'occurence le nom de la section courante) avec celle passée
en argument
(4). Si c'est la bonne section, on l'ouvre puis on boucle directement sur la ligne
suivante sinon, on continue à chercher la bonne section.
Dans le second cas où la section est déjà ouverte, on commence par tester si le premier caractère de la chaîne
n'est pas un début de section. Si tel est le cas, nous avons dépassé la section ouverte sans trouver la clé passée en
argument, on quitte alors la boucle et la fonction renvoie
NULL. On recherche ensuite avec la fonction
strstr
(6) la clé dans la chaîne courante, si la clé est trouvée, on recherche la position du séparateur
(7) avec la fonction
strchr
. Si le séparateur est trouvé, on déplace la position du pointeur de
1
caractère pour se trouver sur le premier caractère de la valeur à récupérer puis on copie la chaîne
(8) dont
le pointeur sera retourné par la fonction.
IV-2.b. Fonction : get_string
char * get_string (const char * section, const char * key)
{
return get (FILENAME, section, key);
}
|
Rien de bien compliqué dans cette fonction, elle renvoie simplement la chaîne retournée par la fonction get.
IV-2.c. Fonction : get_integer
int get_integer (const char * section, const char * key)
{
int ret = 0 ;
char * s = NULL ;
s = get (FILENAME, section, key);
if (s ! = NULL )
{
ret = (int ) strtol (s, NULL , 10 );
free (s);
}
return ret;
}
|
La fonction
get_integer permet de renvoyer une valeur entière, ceci est possible grâce à la conversion de
la chaîne renvoyée
(1) par
get. Si le pointeur retournée est différent de
NULL, on tente une
conversion avec la fonction
strtol
(2) puis on libère la chaîne
(3) qui je le rappel, est
copiée par allocation dynamique.
IV-3. Code source complet
Fichier : conf.ini |
[user]
alias=Franck.H
name=HECHT Franck
[server]
server=192.160.1.10
port=147
[files]
backup=backup.dat
log=log.dat
error=error.dat
|
Fichier : main.c |
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define FILENAME " conf . ini "
# define BUF_SIZE 128
# define BEGIN ' [ '
# define SEP ' = '
static char * str_dup (const char * str)
{
char * dup = NULL ;
if (str ! = NULL )
{
size_t size = strlen (str) + 1 ;
dup = malloc (size);
if (dup ! = NULL )
{
memcpy (dup, str, size);
}
}
return dup;
}
static void str_finalize (char * str)
{
char * p = strchr (str, ' \n ' );
if (p ! = NULL )
{
* p = 0 ;
}
}
static char * get (const char * filename, const char * section, const char * key)
{
char * ret = NULL ;
char buff [BUF_SIZE];
FILE * file = NULL ;
int opened = 0 ;
file = fopen (filename, " r " );
if (file ! = NULL )
{
while ((fgets (buff, BUF_SIZE, file)) ! = NULL )
{
str_finalize (buff);
if (! opened & & buff [0 ] = = BEGIN)
{
char * p = buff;
if (memcmp (p + 1 , section, strlen (buff) - 2 ) = = 0 )
{
opened = 1 ;
continue ;
}
}
else if (opened)
{
if (buff [0 ] = = BEGIN)
{
opened = 0 ;
break ;
}
if (strstr (buff, key) ! = NULL )
{
char * p = strchr (buff, SEP);
if (p ! = NULL )
{
p+ + ;
ret = str_dup (p);
break ;
}
}
}
}
fclose (file);
}
return ret;
}
static char * get_string (const char * section, const char * key)
{
return get (FILENAME, section, key);
}
static int get_integer (const char * section, const char * key)
{
int ret = 0 ;
char * s = NULL ;
s = get (FILENAME, section, key);
if (s ! = NULL )
{
ret = (int ) strtol (s, NULL , 10 );
free (s);
}
return ret;
}
int main (void )
{
char * s = NULL ;
int i = 0 ;
s = get_string (" user " , " alias " );
printf (" %s\n " , s);
free (s);
s = get_string (" server " , " server " );
printf (" %s\n " , s);
free (s);
i = get_integer (" server " , " port " );
printf (" %d\n " , i);
return EXIT_SUCCESS;
}
|
Sortie sur la console |
Franck.H
192.160.1.10
147
|
Copyright © 2009 Franck Hecht.
Aucune reproduction, même partielle, ne peut être faite
de ce site ni de l'ensemble de son contenu : textes, documents, images, etc.
sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à
trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.