Fork me on GitHub

Codage et décodage avec Huffman

Ce DM constitue la suite du TD Construction du code de Huffman. Vous êtes libres d’utiliser le code que vous avez écrit pour le TD, y compris la solution qui y est fournie.

Le but de ce DM est d’écrire un programme permettant le codage et le décodage de textes par le code de Huffman. Le texte à coder/décoder, ainsi que les fréquences d’apparition des symboles sont des entrées pour le programme. Pour simplicité, nous allons utiliser seulement les caractères (ASCII) de ‘a’ à ‘z’.

Plus précisément, votre programme doit:

Dans la suite on va suggérer une façon de structurer votre code, cependant vous n’êtes pas obligés de la suivre à la lettre, tant que votre programme atteint le but recherché.

Flux de bits

La lecture/écriture d’un fichier ASCII peut se faire aussi bien en mode binaire (avec FileInputStream et FileOutputStream), qu’en mode texte (avec FileReader et FileWriter). Allez voir les derniers TDs et la page Entrées-Sorties en Java pour l’usage de ces classes.

Au contraire, l’encodage de Huffman est à longueur variable, et basé sur les bits. Il n’est donc pas commode de lire des fichiers encodés avec un code de Huffman en utilisant les classes par défaut de Java. La première étape consiste à créer des classes permettant de lire ou écrire un flux de données bit par bit.

Un encapsuleur (wrapper en anglais) est une classe qui prend en entrée de son constructeur un objet et qui en étend les fonctionnalités. Des exemples classiques d’encapsuleurs en Java sont BufferedReader et BufferedWriter, qui encapsulent respectivement FileReader et FileWriter, comme dans l’exemple suivant.

BufferedReader in = new BufferedReader(new FileReader("in.txt"));
BufferedWriter out = new BufferedWriter(new FileWriter("out.txt"));

Écrire une classe InputBitStream qui encapsule un InputStream. Elle utilise le InputStream sous-jacent en lisant octet par octet, mais offre une interface permettant de lire un à la fois les bits de chaque octet. Elle pourra contenir, par exemple, les méthodes suivantes

Dans le même esprit, écrire une classe OutputBitStream qui encapsule un OutputStream. Elle pourra contenir, par exemple, les méthodes suivantes

Pour le stockage des bits à l’intérieur d’un octet, vous êtes libres de choisir la Endianness que vous préférez.

Avant de passer à la suite, il est conseillé de bien débugguer cette partie en vérifiant que la lecture et l’écriture bit par bit d’un fichier de texte réalisent une copie parfaite. Pour vous aider à débugguer cette partie du code, vous pouvez utiliser un outil de lecture de fichiers binaires. Sous Linux et MacOS, la commande hd fournit un tel outil. Sous Windows vous pouvez installer Hexdump.

Encoder et décoder

Le pire est passé. Maintenant que vous avez à disposition des flux de bits, encoder revient à

Décoder demande à peine plus d’effort :

Le décodage est légèrement plus compliqué que l’encodage. L’algorithme consiste à parcourir l’arbre de décodage en accord avec les bits provenants du InputBitStream (0 correspond à aller à gauche, 1 à droite). Lorsqu’on atteint une feuille, on écrit le symbole correspondant sur la sortie.

Finalisez votre code avec un main qui permet de choisir le mode d’opération (encodage ou décodage), et qui prend en paramètre les noms des fichiers à lire/écrire.

Challenge

Visitez cette page (testé avec Firefox 10 et Chrome 24) et décodez le message fourni en suivant les instructions.

Envoyez votre code source, ainsi que le texte décodé à l’aide de la boîte de dépot sur e-campus 2 (si e-campus ne devait pas marcher, envoyez-les directement par mail à votre enseignant. La date limite pour envoyer vos fichiers est le mercredi 6 mars à 4h du matin. Un point de pénalité pour chaque heure de retard: le 6 mars à 23h59 c’est votre dernière chance !