Vous êtes sur la page 1sur 6

Université Paris Diderot Projet d’informatique M1BI Année 2008

Projet d’informatique M1BI :


Compression et décompression de texte

Le but de ce projet est de coder un programme réalisant de la compression et décompression de texte. On


se proposera de coder deux algorithmes différents de compression/décompression.
Dans cet énoncé, nous commencerons par rappeler quelques généralités sur la compression de texte, puis nous
présenterons les deux algorithmes à réaliser et enfin nous donnerons quelques informations sur l’organisation du
projet et relatives au rendu et à l’évaluation du projet.

1 Généralités sur la compression/décompression de texte


La compression d’un fichier informatique permet de réduire sa taille, permettant ainsi de gagner de la place
sur le disque dur, de transmettre plus rapidement ce fichier par le réseau, etc. . .. Bien sur, lorsqu’un fichier est
compressé, on doit être capable de le décompresser. On distingue en général deux types de compression pour
les fichiers informatiques : la compression avec pertes ou sans pertes d’informations.
La compression avec pertes d’informations est particulièrement adaptée aux fichiers contenants des données
(( physiques )) tel que du son, de la vidéo ou une image. On ne s’y intéressera pas dans ce projet.
Dans le cas de la compression d’un texte, on ne veut perdre aucune informations. Il ne s’agit pas que, pour
un fichier compressé puis décompressé, un caractère ’a’ du fichier d’origine devienne un caractère ’b’. On
utilise alors des algorithmes de compression dit sans pertes de données. Dans ce projet, il vous est demandé de
coder deux de ces algorithmes, l’algorithme rle (pour Run Length Encoding) et un algorithme se basant sur
les codes de Huffman.

2 L’algorithme RLE.
L’algorithme rle est un algorithme compressant des fichiers contenant des caractères se répétant plusieurs
fois à la suite. L’idée consiste simplement à remplacer une suite du même caractère par ce caractère suivie d’un
chiffre indiquant son nombre de répétitions. Ainsi, la chaı̂ne "aaaa" sera encodée par la chaı̂ne "a3" indiquant
que l’on a le caractère ’a’ suivie de 3 répétitions du même caractère. Cette méthode présente toutefois un
problème. Autant "aaaa" sera compressé, autant "bc" sera encodé par "b0c0", augmentant ainsi les données
au lieu de les compresser.
On propose de modifier légèrement cet algorithme et d’attendre de lire 2 fois de suite le même caractère pour
ensuite indiquer le nombre de répétitions qui suivent. Ainsi, "aaaa" sera encodé par "aa2" et, par exemple,
"aaaabcbb" sera encodé par "aa2bcbb0".
Cette algorithme ne fonctionne bien sûr que si le texte initial contient en moyenne beaucoup de répétitions
successives des caractères.
La décompression est alors relativement simple. On lit les caractères un par un. Tant que l’on ne lit pas
deux fois le même caractère, on se contente d’écrire le caractère lu dans le fichier résultat. Lorsqu’on lit deux
fois de suite le même caractère, on sait que suit un chiffre indiquant le nombre de répétitions restantes pour ce
caractère. Il suffit alors d’écrire autant de fois que nécessaire le caractère en question dans le fichier résultat.

3 L’algorithme de Huffman.
3.1 Principe de l’algorithme
Dans un fichier texte, chaque caractère du texte occupe le même espace physique, soit 8 bits (voir aussi 3.5
pour plus de détails là-dessus). L’idée est de changer cela, en donnant aux caractères les plus fréquents dans
le texte un codage plus court, quitte à donner à certains autres caractère peu fréquent un codage de plus de 8
bits.

1
Université Paris Diderot Projet d’informatique M1BI Année 2008

Pour cela, on va d’abord calculer les probabilités d’apparence de chaque caractère dans le texte à compresser.
Par exemple, supposons que l’on veuille compresser le texte "badbbacddcab", les probabilités1 de chaque car-
actère sont :
caractère probabilité
a 3
b 4
c 2
d 3

L’idée est alors d’associer aux caractères un code (binaire) d’autant moins long que le caractère est fréquent.
On pourrait, pour les probabilités ci-dessus, être tenté de prendre pour b le code 0, pour a le code 1, pour d
le code 10 et pour c le code 11. Ceci serait optimal en terme de longueur des codes mais malheureusement
le décodage du texte (la décompression) serait alors impossible. En effet, avec de tels codes, 101 pourrait être
décodé en "aba" mais aussi en "da".
On doit donc trouver des codes, certes moins optimal, mais tel que le décodage puisse s’effectuer sans
ambiguı̈té. C’est ce que Mr. Huffman (David A. Huffman, 1925-1999) nous propose en utilisant, dans notre
exemple, les codes suivants :
caractère code
a 01
b 00
c 11
d 10
Avec ces codes, 001011 par exemple, ne peut être décodé que par "bdc". Et puisque sur un ordinateur, un
caractère est normalement encodé sur 8 bits, "bdc" occupe 3 ∗ 8 = 24 bits, à comparer avec les 6 bits de son
code de Huffman. On a bien compressé le texte.

3.2 Calcul des codes à l’aide d’arbres de Huffman


Pour calculer ces codes, on va devoir construire un arbre de Huffman. L’arbre de Huffman pour notre exemple
est :
0 1
12

7 5

b (4) a (3) d (3) c (2)

Dans cet arbre, les feuilles sont les caractères (avec leurs probabilités entre parenthèse) alors que les noeuds
représentent des caractères fictifs dont la probabilité est la somme des probabilités de leurs fils. Une fois cet
arbre construit, le code d’un caractère est facilement trouvé en suivant le chemin dans l’arbre depuis la racine
jusqu’au caractère et en prenant 0 lorsque l’on va à gauche et 1 lorsque l’on va à droite.
Comment créer l’arbre de Huffman ? Pour cela, on utilise un tas dont les éléments sont eux-même des arbres
de Huffman. Dans la suite, on appellera probabilité de l’arbre la probabilité du noeud racine d’un arbre donné.
On commence en remplissant le tas avec une feuille pour chaque caractère, et de façon à ce que le tas soit trié
par probabilité croissante. Autrement dit, initialement, l’arbre au sommet du tas contiendra le caractère avec
la plus petite probabilité (dans notre exemple, le caractère c). Ensuite, et tant que le tas contient plus d’un
élément, on récupère les deux derniers éléments du tas (qui sont donc les arbres de plus petites probabilités),
on créer un nouveau noeud fictif ayant pour fils les deux arbres récupérés et comme probabilité la somme des
probabilités de ces deux fils. Enfin, on insère l’arbre ainsi créé dans le tas de façon à ce que ce dernier reste trié
1 Ce qu’on appelle probabilité ici n’est que le nombre d’occurrences de chaque caractère. Pour avoir une probabilité d’apparence

il faudrait diviser ces chiffres par le nombre total de caractère du texte, mais cela ne changerait rien a notre algorithme.

2
Université Paris Diderot Projet d’informatique M1BI Année 2008

par probabilité croissante, et on continue. Lorsqu’il n’y a plus qu’un seul élément sur le tas, celui-ci est l’arbre
de Huffman.
Sur notre exemple précédent, la construction de l’arbre de Huffman suit les étapes suivantes :
On commence avec le tas contenant une feuille par caractère. Le tas est trié en fonction des probabilités :

b (4) a (3) d (3) c (2)

On récupère les deux éléments au sommet du tas, qu’on joint par un noeud fictif de probabilité la somme des
probabilités de ces deux éléments :

b (4) a (3) 5

d (3) c (2)
Puis on insère l’arbre nouvellement créé à sa place dans le tas.

5 b (4) a (3)

d (3) c (2)
On recommence avec les deux nouveaux éléments au sommet.

5 7

d (3) c (2) b (4) a (3)


Et on insère l’arbre nouvellement créé au tas.

7 5

b (4) a (3) d (3) c (2)


Le tas contient plus d’un élément donc on continue.

12

7 5

b (4) a (3) d (3) c (2)


Enfin, lorsqu’il ne reste qu’un seul élément, on a construit notre arbre de Huffman.

12

7 5

b (4) a (3) d (3) c (2)

3.3 Compression
L’algorithme de compression lit en entrée un fichier quelconque et écrit un fichier résultat contenant un
entête suivit de la suite de code de Huffman correspondant au fichier d’entrée. L’algorithme peut se résumer
ainsi :
– On compte, dans le fichier d’entrée, quels caractères apparaissent et combien de fois chacun ;

3
Université Paris Diderot Projet d’informatique M1BI Année 2008

– A partir de ces informations, on construit l’arbre de Huffman ;


– On utilise l’arbre de Huffman pour calculer le code de Huffman de chacun des caractères.
– On écrit l’entête du fichier résultat (voir ci-dessous) ;
– On relit le fichier d’entrée caractère par caractère en écrivant à chaque fois dans le fichier résultat le code
de Huffman correspondant au caractère lue.
Lors de la décompression, on utilisera également l’arbre de Huffman. Mais celui-ci n’est évidemment pas re-
constructible à partir du fichier compressé. Il faut donc sauver cet arbre au début du fichier compressé. Il est
toutefois plus simple de simplement sauver la liste des couples caractère et nombre d’occurrences du caractère
du fichier d’entrée. En effet, une fois récupéré, il suffira d’utiliser la même fonction de construction de l’arbre
de Huffman que pour la compression pour ré-obtenir le même arbre.
Il faut donc écrire cette liste au début du fichier résultat. C’est ce qu’on appelle l’entête du fichier. Une
bonne idée consiste à également conserver le nombre de caractère de la liste, mais aussi le nombre de caractère
total du fichier d’entrée (qui sera donc le nombre total de caractère à décoder à la décompression).

3.4 Décompression
L’algorithme de décompression lit en entrée un fichier compressé avec l’algorithme ci-dessus, et donc com-
prenant un entête suivie d’une suite de code de Huffman, et écrit un fichier correspondant à la décompression
du fichier d’entrée. L’algorithme peut se résumer ainsi :
– On lit l’entête du fichier compressé, récupérant ainsi la liste des caractères avec leurs occurrences, et le
nombre de caractères à décoder ;
– On reconstruit l’arbre de Huffman à partir de la liste lue ;
– On lit alors bit par bit le reste des données du fichier compressé. On va alors se déplacer dans l’arbre de
Huffman. Lorsqu’on lit un 0 on se déplace à gauche, lorsqu’on lit un 1 on se déplace à droite. Et lorsqu’on
arrive à une feuille de l’arbre, on a décodé un caractère : celui de la feuille en question. On écrit alors ce
caractère dans le fichier résultat et on continue avec le bit suivant en repartant du noeud racine de l’arbre
de Huffman ;
– Lorsque l’on a lu le bon nombre de caractère, on a correctement décompressé notre fichier.

3.5 Lecture et écriture de bits dans un fichier


Rappelons qu’un fichier informatique n’est ultimement qu’une (longue) suite de 0 et de 1. Pour un fichier de
texte (ceux auxquels on s’intéresse dans ce projet), on associe à chaque bloc de 8 bits un caractère dit ASCII.
Puisque avec 8 bits on code 256 valeurs, on a 256 caractères ASCII (avec par exemple le code 97 pour le caractère
’a’). Les fonctions de la librairie standard C putc et getc permettent d’écrire et de lire de tels caractères dans
un fichier. Il n’est en revanche pas possible de lire ou d’écrire un seul bit dans un fichier. En particulier, tout
fichier contient un nombre de bit multiple de 8. D’ailleurs, la taille des fichiers informatiques s’exprime toujours
en octet (1 octet = 8 bits). Ainsi un fichier peut toujours être vu comme une suite de caractères.
Toutefois, pour la compression (resp. la décompression) avec l’algorithme de Huffman, on aimerait pouvoir
écrire (resp. lire) dans un fichier bit par bit. Il va donc falloir (( émuler )) cela en utilisant un buffer accumulant
les bits à écrire et, lorsque l’on a accumulé 8 bits, on les écrits (avec putc). De même, pour la lecture, on lira 8
bits d’un coup (avec getc) et on récupéra ensuite les bits un par un. Et lorsque les 8 bits auront été récupérés,
on relira 8 nouveaux bits du fichier d’entrée. Pour cela on aura besoin en particulier des opérations de décalage
de bit >> et << ainsi que des opérations bit à bit &, |, etc. . .
Il faudra faire attention au fait que la compression avec l’algorithme n’engendre pas nécessairement un
nombre de bits totales multiple de 8.

4 Réalisation du projet
Le but du projet est d’écrire un programme C effectuant la compression et la décompression d’un fichier
avec l’algorithme rle et l’algorithme basé sur les codes de Huffman. Pour cela, le projet comporte 2 parties.
La première partie consiste à coder l’algorithme rle et fait office d’échauffement. La seconde partie concerne
Huffman.

4
Université Paris Diderot Projet d’informatique M1BI Année 2008

4.1 1ère partie : RLE.


Vous coderez la compression et la décompression avec l’algorithme rle. Vous devrez donc coder 2 fonctions,
une de compression et une de décompression, mais également une fonction main résultant, une fois compilée,
en un programme permettant la compression ou la décompression (une option sur la ligne de commande devra
permettre ce choix) d’un fichier.

4.2 2nde partie : Huffman.


Pour cet algorithme, on a besoin de manipuler des structure de données, les arbres de Huffman et les tas
d’arbres de Huffman. On doit aussi être capable de lire et d’écrire des bits. On va donc procéder dans l’ordre
suivant :
1. Vous coderez une petite librairie sur les arbres de Huffman. Le type des arbres de Huffman sera :
typedef struct hnode {
int fictive; /* Vaudra 0 si le noeud n’est pas fictif, 1 sinon. */
int character; /* Le caractère si le noeud n’est pas fictif. */
int proba; /* La probabilité du noeud. */
/* Ici vous pourrez rajouter tous les champs qui vous semblent utiles. */
struct hnode *left;
struct hnode *right;
} hnode_t;

typedef hnode_t *htree;

Vous devrez coder au moins les fonctions suivantes :


– des fonctions d’accès et de modifications des différents éléments contenus dans l’arbre ;
– une fonction de création d’un arbre ne contenant qu’une feuille ;
– une fonction qui joint deux arbres par un noeud fictif ayant comme probabilité la somme des probabilités
des deux sous-arbres.
2. Vous coderez une petite librairie sur les tas d’arbres de Huffman. Le type des tas sera :
typedef struct helt {
htree elt;
struct helt *next;
} helt_t;

typedef helt_t *hheap

Vous coderez au moins les fonctions suivantes :


– une fonction de création d’un tas vide ;
– des fonctions pour pousser/retirer l’élément au sommet du tas ;
– un fonction d’insertion d’un arbre dans un tas respectant l’ordre en probabilité croissante du tas ;
– une fonction qui teste si un tas ne contient plus qu’un seul élément.
3. Vous coderez ensuite une petite librairie pour la lecture et l’écriture de bits. Vous devrez au moins coder
les fonctions suivantes :
– une fonction de lecture d’un bit depuis un descripteur de fichier (type FILE*) ;
– une fonction d’écriture d’un bit dans un descripteur de fichier ;
– une fonction terminant l’écriture de bits (en effet, on écrira réellement le fichier seulement lorsque l’on
aura collecté 8 bits. Il est toutefois possible qu’à la fin, on ait moins de 8 bits à écrire. Cette fonction
permettra alors d’écrire quand même les derniers bits.).
Lorsque vous aurez codé ces 3 librairies, vous coderez des fonctions de compression et de décompression d’un
fichier avec l’algorithme de Huffman. Pour finir, vous intégrerez la compression/décompression avec Huffman
au programme que vous aviez écrit dans la première partie.

5
Université Paris Diderot Projet d’informatique M1BI Année 2008

5 Organisation du projet
Le projet doit être réalisé en monôme. Un soin tout particulier devra être apporté à l’indentation et le code
devra être commenté correctement. On évitera également de coder l’intégralité du projet dans un seul fichier. La
date limite de rendu du projet est fixée au Vendredi 15 Février, 23h59. Des soutenances seront organisées
la semaine du 18 Février. Le rendu s’effectuera par mail à l’adresse sylvain.lebresne@pps.jussieu.fr. Ce
mail devra contenir :
– Tous les fichiers source (.c et .h) de votre projet. Chaque fichier source devra commencer par votre nom
et prénom en commentaire ;
– Un fichier Makefile permettant la compilation de votre projet, et ce simplement à l’aide de la commande
make ;
– Un simple fichier texte expliquant en quelques lignes comment utiliser le (les) binaire(s) créé(s) lors de la
compilation ;
– Un document de quelques pages expliquant les difficultés que vous avez rencontrées et les solutions que
vous y avez apportées.

Vous aimerez peut-être aussi