École de technologie supérieure INF-145 Travail pratique #3 Frédérick Henri Travail en équipe de 2 L algorithme de compression de LZW 1 Objectifs Retour sur les notions de pointeur, d enregistrement et de fichier binaire. Mise en place d une petite liste chaînée. Apprentissage d un algorithme de compression. 2 Description du problème: Compresser un fichier? La compression consiste à prendre un fichier et à diminuer sa taille. Cela a pour effet de rendre le fichier inutilisable (comme c est le cas pour un.zip) ou de dégrader sa qualité (comme c est le cas pour un.jpg ou.mp3). Certains diront que la compression permet d écrire sur un support (disquette ou CD) un document qui autrement ne pourrait y être inscrit en raison de sa grosseur. Cependant, dans cette ère où la télécommunication s infiltre partout, le principal avantage de la compression repose sur l équation suivante : Temps de compression + Temps de transmission du document compressé + Temps de décompression < Temps de transmission du document non compressé Votre travail consiste à écrire un programme en C permettant de compresser un fichier. Vous devez utiliser l algorithme LZW décrit ci-dessous et utiliser les modules spécifiés. Prenez note que ce travail porte sur un algorithme servant à la compression de plusieurs types de fichiers dont les.gif, les.tiff et les.zip. INF-145 Programmation avancée et langage C Automne 2013 / Page 1
3 Description de l algorithme de compression de Lempel Ziv Welch Voici l algorithme général suivi d une trace pour bien saisir son fonctionnement : chaîne : t_chaîne // Une chaîne pouvant contenir '\0'. caractère : t_caractère // Un caractère non signé. dictionnaire : t_dictionnaire // Un dictionnaire de chaînes. ajouter tous les codes ASCII au dictionnaire tant que (on peut lire un caractère dans le fichier source) caractère lire_un_caractère(fichier_source) si (dictionnaire contient la chaîne chaîne+caractère) alors chaîne chaîne+caractère // On concatène les deux. sinon si (le dictionnaire contient moins de 2 12 chaînes) alors ajouter chaîne+caractère dans le dictionnaire fin si écrire le code de chaîne dans le fichier destination chaîne caractere; fin si fin tant que écrire le code de chaîne dans le fichier destination Pour bien saisir l exemple qui suit, il faut savoir que lorsqu'on écrit le code de la chaîne dans le fichier destination, celui-ci prend 12 bits. En effet, l objectif de l algorithme est de trouver des chaînes qui se répètent et de les remplacer par un nombre (un code). Puisqu il y a en partant 256 chaînes possibles (les codes ASCII), on ne peut pas conserver ces codes sur 8 bits. Nous encoderons donc les chaînes sur 12, 13 ou 14 bits (arbitrairement, nous choisissons 12). Voici un exemple de fonctionnement de l algorithme où les écritures dans le fichier destination sont présentées sous forme de lettres et de bits pour mieux comprendre : INF-145 Programmation avancée et langage C Automne 2013 / Page 2
Exemple de compression Contenu du fichier source : Remplissage du dictionnaire : LE PAPA PALE 0 = "\0",, 48 = "0",, 97 = "a", etc. Contenu de chaîne : Caractère lu dans le fichier source : Ajout au dictionnaire : Écriture binaire dans le fichier Équivalent de ce qui a été écrit (pour la compréhension) "" (vide) 'L' - - - "L" 'E' 256 = "LE" 000001001100 76 = "L" "E" ' ' (blanc) 257 = "E " 000001000101 69 = "E" " " (blanc) 'P' 258 = " P" 000000100000 32 = " " (blanc) "P" 'A' 259 = "PA" 000001010000 80 = "P" "A" 'P' 260 = "AP" 000001000001 65 = "A" "P" 'A' - - - "PA" " " (blanc) ' ' (blanc) " P" 'A' 261 = "PA " 000100000011 259 = "PA" 'P' - - - 262 = " PA" 000100000010 258 = " P" "A" 'L' 263 = "AL" 000001000001 65 = "A" "L" 'E' - - - "LE" - - 000100000000 256 = "LE" Ainsi, le fichier destination contiendra les bits suivants : 000001001100 000001000101 000000100000 000001010000 000001000001 000100000011 000100000010 000001000001 000100000000 qui correspondent à écrire les entiers 76, 69, 32, 80, 65, 259, 258, 65 et 256 sur 12 bits chacun. INF-145 Programmation avancée et langage C Automne 2013 / Page 3
4 Description de l algorithme de décompression de Lempel Ziv Welch Voici l algorithme général permettant d effectuer la décompression d un fichier compressé à l aide de l algorithme précédent. chaîne : t_chaîne // Une chaîne pouvant contenir \0. caractère : t_caractère // Un caractère non signé. dictionnaire : t_dictionnaire // Un dictionnaire de chaînes. vieux_code : entier nouveau_code : entier ajouter tous les codes ASCII au dictionnaire vieux_code lire_un_code(fichier_source) chaîne chercher la chaîne du vieux_code dans le dictionnaire écrire_une_chaîne(chaîne, fichier destination) caractère premier caractère de la chaîne tant que (on peut lire un code dans le fichier source) nouveau_code lire_code(fichier_source) si (nouveau_code n est pas present dans le dictionnaire) alors chaîne chercher la chaîne du vieux_code dans le dictionnaire chaîne chaîne + caractère // On concatène. sinon chaîne chercher la chaîne du nouveau_code dans le dictionnaire fin si écrire_une_chaîne(chaîne, fichier destination) caractère premier caractère de chaîne chaîne chercher la chaîne du vieux_code dans le dictionnaire chaîne chaîne + caractère si (le dictionnaire contient moins de 2 12 chaînes) alors ajouter la chaîne dans le dictionnaire fin si vieux_code nouveau_code fin tant que INF-145 Programmation avancée et langage C Automne 2013 / Page 4
Exemple de décompression Contenu du fichier source : 000001001100000001000101000000100000 (voir le résultat de la compression ci-dessus) Remplissage du dictionnaire : 0 = "\0",, 48 = "0",, 97 = "a", etc. AVANT LA BOUCLE : vieux_code 000001001100 (76 = "L") chaîne "L" écriture de "L" dans le fichier destination caractère 'L' BOUCLE (1 ère passe) nouveau_code 000001000101 (69 = "E") chaîne "E" écriture de "E" dans le fichier destination caractère 'E' chaîne "LE" ajout de 256 = "LE" dans le dictionnaire vieux_code 69 BOUCLE (2 e passe) nouveau_code 000000100000 (32 = " ") chaîne " " écriture de " " dans le fichier destination caractère ' ' chaîne "E " ajout de 257 = "E " dans le dictionnaire vieux_code 32 BOUCLE (3 e passe) nouveau_code 000001010000 (80 = "P") chaîne "P" écriture de "P" dans le fichier destination caractère 'P' chaîne " P" ajout de 258 = " P" dans le dictionnaire vieux_code 80 INF-145 Programmation avancée et langage C Automne 2013 / Page 5
BOUCLE (4 e passe) nouveau_code 000001000001 (65 = "A") chaîne "A" écriture de "A" dans le fichier destination caractère 'A' chaîne "PA" ajout de 259 = "PA" dans le dictionnaire vieux_code 65 BOUCLE (5 e passe) nouveau_code 000100000011 (259 = "PA") chaîne "PA" écriture de "PA" dans le fichier destination caractère = 'P' chaîne = "AP" ajout de 260 = "AP" dans le dictionnaire vieux_code 259 BOUCLE (6 e passe) nouveau_code 000100000010 (258 = " P") chaîne = «P». écriture de " P" dans le fichier destination caractere ' ' chaîne "PA " ajout de 261 = "PA " dans le dictionnaire vieux_code 258 BOUCLE (7 e passe) nouveau_code 000001000001 (65 = "A") chaîne "A". écriture de "A" dans le fichier destination caractère "A" chaîne " PA" ajout de 262 = " PA" dans le dictionnaire vieux_code 65 BOUCLE (8 e passe) FIN nouveau_code 000100000000 (256 = "LE") chaîne "LE". écriture de "LE" dans le fichier destination caractère "L" chaîne "AL" ajout de 263 = "AL" dans le dictionnaire vieux_code 256 INF-145 Programmation avancée et langage C Automne 2013 / Page 6
5 Recommandations et exigences supplémentaires Puisque la compression LZW est fortement documentée sur le web (une simple recherche avec les termes LZW data compression algorithm vous le démontrera), l implantation que nous vous demandons est un peu particulière. Elle sera TRÈS lente, mais vous amènera à pratiquer davantage la création de type et le découpage en librairie et évitera que vous ne preniez votre code directement du web ÉTAPE 1 : Module chaine Date de complétion : 14 novembre Votre programme doit pouvoir compresser des fichiers binaires. Or, puisque les fichiers binaires peuvent contenir des séquences de '\0', les chaînes du C ne conviennent pas à l implémentation de l algorithme. En effet, on sait que le caractère '\0' indique une fin de chaîne. Cela ne convient pas du tout à notre application. Le.h d un module implémentant un type t_chaine permettant de conserver une chaîne pouvant contenir des '\0' vous est offert. Vous n avez qu à coder le.c de ce module. Prenez note qu il vous est interdit de modifier le.h et qu afin de respecter le principe de la représentation cachée tout client de ce module ne peut accéder au contenu d un objet de type t_chaine qu en utilisant les fonctions du module. À noter, il se peut que vous ne puissiez pas faire immédiatement la fonction ecrire_chaine_fichier car vous ne verrez pas la manipulation de fichiers avant la onzième semaine. ÉTAPE 2 : Module liste Date de complétion : 21 novembre Votre programme doit conserver un dictionnaire de codes (des chaînes de caractères auxquelles sont associés des numéros). Afin de simplifier la création du dictionnaire, nous utiliserons une liste chaînée de t_chaine. Le.h d un module implémentant un type t_liste_chainee permettant de conserver des chaînes dans une liste chaînée vous est offert. Vous n avez qu à coder le.c de ce module. Prenez note qu il vous est interdit de modifier le.h et qu afin de respecter le principe de la représentation cachée tout client de ce module ne peut INF-145 Programmation avancée et langage C Automne 2013 / Page 7
accéder au contenu d un objet de type t_liste_chainee qu en utilisant les fonctions du module. ÉTAPE 3 : Module dictionnaire Date de complétion : 28 novembre Un dictionnaire permet de conserver toutes les chaînes ainsi que le code associé à chacune d elles. Afin de rendre l ajout et la recherche plus rapide, nous regrouperons deux tableaux de listes chaînées dans un enregistrement. Le premier tableau possédera 256 éléments dont chacune des listes contiendra des chaînes débutant par un code ASCII identique à l indice. Ainsi, la chaîne "LE" se trouvera dans la liste à l indice 76 puisque le code ASCII du premier caractère ('L') est 76. Le second tableau possédera 100 éléments dont chacune des listes contiendra des chaînes dont le code modulo 100 donne l indice. Ainsi, si la chaîne "abc" possède le code 437, alors elle se trouve dans la liste à l indice 37, puisque 437 mod 100 = 37. Chaque chaîne est donc présente à deux reprises dans le dictionnaire. Le.h d un module implémentant un type t_dictionnaire vous est offert. Vous n avez qu à coder le.c de ce module. Prenez note qu il vous est interdit de modifier le.h et qu afin de respecter le principe de la représentation cachée tout client de ce module ne peut accéder au contenu d un objet de type t_dictionnaire qu en utilisant les fonctions du module. ÉTAPE 4 : Programme principal Date de complétion : 6 décembre Votre programme principal doit saisir le nom du fichier à compresser et le nom du fichier de destination directement sur la ligne de commande. Ensuite, si l usager demande de compresser, il compresse le fichier et sinon il le décompresse. Le tout doit se faire à l aide de l algorithme offert précédemment. La majorité des fonctions vous sont offertes ; vous devez compléter les fonctions effectuer_compression et effectuer_decompression. Au besoin, vous devrez créer des sous-fonctions. INF-145 Programmation avancée et langage C Automne 2013 / Page 8
6 Contraintes de l enseignant La remise du travail complet devra respecter les exigences de remise des travaux pratiques (voir sur le site du cours dans la section Travaux pratiques). De plus, AUCUNE VARIABLE GLOBALE NE SERA ACCEPTÉE. CECI ENTRAINERA UNE PERTE DU QUART DES POINTS (25%). Un programme qui ne compile pas se voit attribuer la note zéro pour la partie exécution. Un programme n utilisant pas l algorithme LZW se voit attribuer la note 0. Un programme n utilisant pas les modules fournis se voit attribuer la note 0. La politique du 10% pour la qualité du français sera appliquée. Aucun document écrit à la main ne sera accepté. Une partie qui n'est pas imprimée n'est pas corrigée (même si elle est sur la disquette) et se voit attribuer la note 0. BON TRAVAIL! INF-145 Programmation avancée et langage C Automne 2013 / Page 9