Projet de Développement - PrDv 1 La compression de fichiers par codage de Huffman 1) Le projet Le but du projet est de réaliser un logiciel de compression de fichiers, comme WinZip ou WinRar, en utilisant le codage de Huffman1. Le principe du codage de Huffman est de choisir une représentation de l’information de longueur variable permettant ainsi de représenter des symboles apparaissant fréquemment par une représentation courte, quitte à ce que certains symboles peu fréquents disposent, eux, d'une représentation plus longue. Pour la compression de fichiers, les symboles peuvent être les caractères (ou plus généralement, les octets) contenus dans le fichier. Pour simplifier la suite, nous parlerons de fichiers textes et de caractères, même si l'algorithme s'appliquera également aux autres situations. Le codage de Huffman est donc un codage symbole par symbole, sans perte d’information. Il peut être montré qu’en ce sens, il est optimal. Tous les logiciels de compression populaires utilisent, d’une façon ou d’une autre, ce codage. 2) Exercice préparatoire : Entrées/Sorties bit par bit Une caractéristique d'un programme mettant en œuvre le codage de Huffman pour compresser des fichiers est la nécessité de lire et d'écrire, dans un fichier, des séquences binaires de longueur variable. C'est une difficulté particulière car les langages de programmation ne prévoient, normalement que des Entrées/Sorties (E/S) fixes (ou, éventuellement, des E/S de chaînes de caractères qui, elles, peuvent être de longueur variable). Tant au niveau des accès mémoire que des E/S, la plus petite unité adressable des ordinateurs actuels est l'octet (séquence de 8 bits). Toute donnée est donc normalement représentée par un multiple d'un octet. Ce n'est plus le cas pour un codage de Huffman. Nous aurons donc besoin de nous construire une librairie de routines, capable de traiter, un bit à la fois, les informations en E/S. Nous allons donc faire de l'algèbre binaire. Les opérateurs binaires Le langage Pascal, comme la plupart des langages de programmation, dispose de plusieurs opérateurs permettant de faire de l'algèbre binaire. En Pascal, certains de ces opérateurs s'écrivent de la même façon que les opérateurs logiques que vous connaissez. Attention de ne pas les confondre : Les opérateurs logiques s'appliquent à des opérandes Booléens alors que les opérateurs binaires s'appliquent aux entiers. Les opérateurs binaires sont : 2.1 Opérateur Opération Exemple not Négation binaire (inversion de chaque bit) not x and Et bit à bit (bit résultat à 1 ssi les 2 bits correspondants le sont) x and y or Ou bit à bit (bit résultat à 0 ssi les 2 bits correspondants le sont) x or y xor Ou exclusif bit à bit (bit résultat à 0 ssi les 2 bits correspondants sont égaux) x xor y shl Décalage binaire vers la gauche d'un certain nombre de bits x shl y shr Décalage binaire vers la droite d'un certain nombre de bits x shr y 1 http://fr.wikipedia.org/wiki/Codage_de_Huffman EPFC - ULB Bachelier en Informatique 2012 Projet de Développement - PrDv 2 Par exemple si x est un Byte (entier non-signé stocké sur un octet et prenant des valeurs de 0 à 255) de valeur 202 (binaire : 11001010) et y de valeur 172 (binaire 10101100), alors, l'opération x And y donnera : 11001010 10101100 ———————— And 10001000 C'est-à-dire le résultat 136. En C++ et en C#, les opérateurs correspondants sont : ~ (not), & (and), | (or), ^ (xor), << (shl) et >> (shr). En Java, utilisez les opérateur semblables. Attention, en Java, utilisez >>> pour le décalage à droite (et pas >>). Pour d'autres langages, voyez le manuel de référence. Dans un langage qui le permet, veillez à utiliser des entiers non-signés (Byte, Word, LongWord en Pascal). Manipulation des fichiers en Pascal Un programme ByteCopy.pas vous est fourni pour vous illustrer la manipulation des fichiers en Pascal. Ce programme recopie, octet par octet, le contenu d'un fichier. Vous y verrez l'usage des routines d'E/S du Pascal : 2.2 • AssignFile qui associe une variable fichier à son nom externe • Reset qui ouvre un fichier existant • Rewrite qui crée et ouvre un nouveau fichier • Read et Write qui permettent de lire et d'écrire dans un fichier • FileSize qui renvoie la taille d'un fichier • CloseFile qui ferme un fichier (et vide son tampon) Pour les autres langages, reportez-vous au manuel de référence. Un programme ByteCopy.java vous est aussi fourni pour illustrer une façon possible, en Java, de recopier, octet par octet, le contenu d’un fichier. Une librairie d'E/S bit par bit L'algorithme de Huffman nécessitera d'écrire, bit par bit, le résultat du codage dans le fichier compressé. Il s'agira aussi, plus tard, de relire bit par bit les information dans le fichier compressé pour reconstruire le fichier d'origine. 2.3 Bien sûr, il n’est pas question de lire et d’écrire vraiment un bit à la fois dans un fichier. L’idée est donc d’utiliser un tampon (une variable intermédiaire) de 1 octet. Par exemple, pour l’écriture dans un fichier, chaque écriture d’un bit ne fera que mémoriser la valeur du bit dans le tampon. Ce n’est que lorsque le tampon sera plein (c’est-à-dire que 8 bits y auront été ajoutés) que le tampon sera écrit dans le fichier et réinitialisé afin d'être ainsi prêt à recevoir les bits suivants à écrire dans le fichier. Un processus similaire est utilisé pour la lecture. EPFC - ULB Bachelier en Informatique 2012 Projet de Développement - PrDv 3 Au minimum, vous devrez donc réaliser une librairie (unité en Pascal) dont l'Interface est la suivante : Unit BitFile; Interface // Un fichier qu'on traite un octet à la fois Type FichierBit = ... // A vous de compléter // Crée l'association du fichier f avec son NomFich Procedure AssocieNomFich(Var f: FichierBit; Const NomFich: String); // Préparation du fichier pour une lecture bit à bit. // Initialise le tampon de lecture Procedure InitFichLecture(Var f: FichierBit); // Préparation du fichier pour une écriture bit à bit. // Initialise le tampon de d'écriture Procedure InitFichEcriture(Var f: FichierBit); // Ecriture du bit dans le (tampon du) fichier f Procedure EcrireBit(Var f: FichierBit; bit: Byte); // Ecriture de l'octet (8 bits) dans le (tampon du) fichier f Procedure EcrireByte(Var f: FichierBit; octet: Byte); // Lecture d'un bit à partir du (tampon du) fichier f Procedure LireBit(Var f: FichierBit; Var bit: Byte); // Lecture d'un octet (8 bits) à partir du (tampon du) fichier f Procedure LireByte(Var f: FichierBit; Var octet: Byte); // Vidage final du tampon du fichier f Procedure ClotureEcriture(Var f: FichierBit); A nouveau, un programme BitCopy.pas vous est fourni pour illustrer l'usage de la librairie BitFile et qui recopie, bit par bit, le contenu d'un fichier (texte), cette fois, en ajoutant une ligne de début et une de fin. Le programme pourra vous servir à tester votre implémentation de l’unité BitFile. Si vous décidez d’utiliser un autre langage de programmation, vous concevrez une librairie similaire, appropriée à votre langage. Vous êtes libre de choisir un design qui utilise les constructions du langage de votre choix (Classes, Interfaces, Méthodes…) pour autant que vous respectiez l’idée des fonctionnalités offertes par l’unité Pascal ci-dessus. EPFC - ULB Bachelier en Informatique 2012