HUFFMAN
Description
Ce programme permet de compresser et décompresser un fichier en utilisant
l'algorithme de Huffman. Les taux de compression obtenus sont bien évidemment
minables par rapport à zip ou rar, mais bon, comme on dit, l'important
c'est de participer !
Syntaxe
huffman c/d/f source destination [frequence]
- c pour compresser le fichier source dans destination.
- d pour le décompresser
- f pour créer un fichier de fréquences
L'option frequence permet de spécifier un fichier de fréquences
qui sera utilisé pour compresser le fichier source.
Fixed
11/01/2003 : la variable nbre_ascii (contenant le nombre de caractères
ascii différents présents dans le fichier) était codée
sur 8 bits ; il y avait donc un dépassement de capacité lorsque
le fichiers contenait les 256 caractères du code ascii...
Compilation
Le programme peut tout aussi bien etre compilé sous Windows ou Linux.
/* Outil de compactage/décompactage de fichier utilisant le codage de Huffman Compilation sous windows avec Visual C++ Compilation sous Linux : gcc -DLINUX_COMPIL huffman.c -o huffman */ #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef LINUX_COMPIL /* Compilation conditionnelle */ #include <sys/time.h> #endif #define CODE_MAX_LEN 32 #define BUF_LEN 512 /* Definition des structures */ struct arbre { short branche0; short branche1; }; struct arbre_data { unsigned long frequence; short index_suivant; }; struct dictionnaire { unsigned char taille; char code[CODE_MAX_LEN]; }; /* Prototypes des fonctions */ short huffman_calculer_frequences(FILE *, unsigned long *, unsigned short *); short huffman_lire_frequences(FILE *); short huffman_creer_arbre(short); void huffman_creer_dictionnaire(unsigned char *, short, short); char huffman_creer_fichier_frequences(FILE *, FILE *); char huffman_compacter(FILE *, FILE *, FILE *); char huffman_decompacter(FILE *, FILE *, FILE *); /* Variables globales */ struct arbre_data *arbre_d; struct arbre *arbre; struct dictionnaire *dico; /* Calcule la fréquence de chaque caractère dans un fichier et trie la liste de structures par fréquences croissantes *src : pointeur sur le fichier *nbre_octets : pointeur sur une variable recevant la taille du fichier *nbre_ascii : pointeur sur une variable recevant le nombre de caractères différents présents dans le fichier source Retourne l'index de la première structure dans la liste triée */ short huffman_calculer_frequences(FILE *src, unsigned long *nbre_octets, unsigned short *nbre_ascii) { int i, r; unsigned char buffer[BUF_LEN]; short index_frequence=0, index_precedent=-1; int continuer=1; short c1, c2; *nbre_octets=0; *nbre_ascii=0; memset(arbre_d, 0, 512*sizeof(struct arbre_data)); /*Lecture dans le fichier */ while ((r=fread(buffer, 1, BUF_LEN, src))>0) { *nbre_octets+=r; for(i=0; i<r; i++) /* incrémentation du compteur du caractère correspondant */ arbre_d[buffer[i]].frequence++; } /* Chainage des structures avec une fréquence supérieure à 0 */ for(i=0; i<256; i++) if (arbre_d[i].frequence>0) { (*nbre_ascii)++; if (index_precedent==-1) index_frequence=i; else arbre_d[index_precedent].index_suivant=i; index_precedent=i; } if (index_precedent==-1) index_frequence=-1; else arbre_d[index_precedent].index_suivant=-1; /* Tri des structures (bubble sort) */ while (continuer) { c1=index_frequence; continuer=0; index_precedent=-1; while(c1!=-1) { if ((c2=arbre_d[c1].index_suivant)!=-1) { if (arbre_d[c1].frequence>arbre_d[c2].frequence) { continuer=1; if (index_precedent==-1) index_frequence=c2; else arbre_d[index_precedent].index_suivant=c2; arbre_d[c1].index_suivant=arbre_d[c2].index_suivant; arbre_d[c2].index_suivant=c1; } index_precedent=c1; c1=c2; } else c1=c2; } } /* On retourne l'index de la première structure */ return index_frequence; } /* Lit les fréquences de chaque caractère, inscrites soit dans le fichier compressé, soit dans un fichier de fréquences spécial */ short huffman_lire_frequences(FILE *frq) { unsigned short nbre_ascii; unsigned char i; short index_frequence=-1, index_precedent=-1; /* Lecture de la taille de la table des fréquences */ fread(&nbre_ascii, 2, 1, frq); /* Lecture de la table des fréquences */ while (nbre_ascii>0) { /* Lecture du caractère en cours */ fread(&i, 1, 1, frq); /* Lecture de la fréquence du caractère en cours */ fread((char *)&arbre_d[i].frequence, 4, 1, frq); /* Chainage de la structure */ if (index_frequence==-1) index_frequence=i; else arbre_d[index_precedent].index_suivant=i; index_precedent=i; nbre_ascii--; } if (index_precedent==-1) return -1; arbre_d[index_precedent].index_suivant=-1; return index_frequence; } /* Crée un arbre à partir d'une liste de fréquences index_fréquence : idnex de la première structure dans la liste *arbre_d Retourne l'index de la racine de l'arbre dans la liste *arbre */ short huffman_creer_arbre(short index_frequence) { short i, j, j_save; unsigned long somme_frequence; short nbre_noeuds=256; char struct_inseree=0; /* Les structures 0 à 255 correspondent aux caractères, ce sont des terminaisons => -1 */ for(j=0; j<256; j++) arbre[j].branche0=arbre[j].branche1=-1; /* Création de l'arbre : La mise en commun les deux fréquences les plus faibles crée un nouveau noeud avec une frequence égale a la somme des deux fréquences. Il s'agit ensuite d'insérer cette nouvelle structure dans la liste triée */ i=index_frequence; while(i!=-1) { if (arbre_d[i].index_suivant==-1) { /*printf("Arbre cree\n");*/ break; } /*printf("%d\n", arbre_d[i].frequence); printf("%d\n", arbre_d[arbre_d[i].index_suivant].frequence); */ somme_frequence=arbre_d[i].frequence + arbre_d[arbre_d[i].index_suivant].frequence; /*printf("Nouveau noeud : %d (%d) et %d (%d) => %d\n", i, arbre_d[i].frequence, arbre_d[i].index_suivant, arbre_d[arbre_d[i].index_suivant].frequence, somme_frequence);*/ arbre_d[nbre_noeuds].frequence=somme_frequence; arbre[nbre_noeuds].branche0=arbre_d[i].index_suivant; arbre[nbre_noeuds].branche1=i; /* Insertion du nouveau noeud dans la liste triée */ j_save=-1; struct_inseree=0; j=i; while (j!=-1 && struct_inseree==0) { if (arbre_d[j].frequence>=somme_frequence) { if (j_save!=-1) arbre_d[j_save].index_suivant=nbre_noeuds; arbre_d[nbre_noeuds].index_suivant=j; /*printf("Insertion du nouveau noeud : entre %d et %d\n", j_save==-1?-1:arbre_d[j_save].frequence, arbre_d[j].frequence);*/ struct_inseree=1; } j_save=j; j=arbre_d[j].index_suivant; } /* Insertion du nouveau noeud a la fin */ if (struct_inseree==0) { arbre_d[j_save].index_suivant=nbre_noeuds; arbre_d[nbre_noeuds].index_suivant=-1; /*printf("Insertion du nouveau noeud à la fin : %d\n", arbre_d[j_save].frequence);*/ } nbre_noeuds++; i=arbre_d[i].index_suivant; i=arbre_d[i].index_suivant; } /* On retourne l'index du noeud racine */ return nbre_noeuds-1; } /* Procédure récursive qui crée un dictionnaire (correspondance entre la valeur ascii d'un caractère et son codage obtenu avec la compression huffman) à partir d'un arbre *code : pointeur sur une zone mémoire de taille CODE_MAX_LEN recevant temporairement le code, au fur et à mesure de la progression dans l'arbre index : position dans l'arbre (index de la structure courante) pos : nombre de bits deja inscrits dans *code */ void huffman_creer_dictionnaire(unsigned char *code, short index, short pos) { /* On a atteint une terminaison de l'arbre : c'est un caractère */ if ((arbre[index].branche0==-1) && (arbre[index].branche1==-1)) { /* Copie du code dans le dictionnaire */ memcpy(dico[index].code, code, CODE_MAX_LEN); /*printf("%c: %x - %d\n", index, code[0], pos);*/ /* taille du code en bits */ dico[index].taille=(unsigned char)pos; } /* le noeud possède d'autres branches : on continue à les suivre */ else { /* On suit la branche ajoutant un bit valant 0 */ code[pos/8]&=~(0x80>>(pos%8)); /* Le "(short)" devant "(pos+1)", c'est juste pour empecher VC++ de chipoter (Warning : integral size mismatch in argument : conversion supplied) */ huffman_creer_dictionnaire(code, arbre[index].branche0, (short)(pos+1)); /* On suit la branche ajoutant un bit valant 1 */ code[pos/8]|=0x80>>(pos%8); huffman_creer_dictionnaire(code, arbre[index].branche1, (short)(pos+1)); } } /* Crée une table de fréquences à partir de src, l'inscrit dans dst, puis affiche un tableau des caractères de la table, avec leur fréquence. */ char huffman_creer_fichier_frequences(FILE *src, FILE *dst) { short i, compteur=0; unsigned long nbre_octets; unsigned short nbre_ascii=0; i=huffman_calculer_frequences(src, &nbre_octets, &nbre_ascii); /*Ecriture de la taille de la table des fréquences */ fwrite(&nbre_ascii, 1, 1, dst); printf("Création du fichier de fréquences...\n"); printf("%d caractères représentés sur 256\n", nbre_ascii); /*Ecriture de la table des fréquences */ while(i!=-1) { nbre_ascii=i; fwrite(&nbre_ascii, 1, 1, dst); fwrite((char *)&arbre_d[i].frequence, 4, 1, dst); if ((nbre_ascii>=32 && nbre_ascii<=126) || (nbre_ascii>=161 && nbre_ascii <=255)) printf("%-4c %-6d", nbre_ascii, arbre_d[i].frequence); else printf("0x%02x %-6d", nbre_ascii, arbre_d[i].frequence); if (((compteur+1)%7)==0) printf("\n"); compteur++; i=arbre_d[i].index_suivant; } printf("\n"); return 0; } /* Compresse le fichier src. Le résulat se trouve dans le fichier dst Si frq est différent de NULL, il est utilsé pour lire la table des fréquences nécessaire à la construction de l'arbre */ char huffman_compacter(FILE *src, FILE *dst, FILE *frq) { int i, r, octet_r, bit_r, bit_count, bit_w; unsigned long nbre_octets; short index_frequence; short racine_arbre; unsigned short nbre_ascii=0; unsigned char code[CODE_MAX_LEN]; unsigned char buffer[BUF_LEN]; #ifdef LINUX_COMPIL struct timeval tv; unsigned int t_debut; gettimeofday(&tv, NULL); t_debut=tv.tv_usec; #endif printf("Compression en cours...\n"); /* création ou lecture de la table des fréquences */ if (frq==NULL) { index_frequence=huffman_calculer_frequences(src, &nbre_octets, &nbre_ascii); } else { printf("Utilisation d'un fichier de fréquences pour la création du dico\n"); fseek(src, 0, SEEK_END); nbre_octets=ftell(src); index_frequence=huffman_lire_frequences(frq); } /*Ecriture de la taille en octets du fichier original */ fwrite((char *)&nbre_octets, 4, 1, dst); /*Ecriture de la taille de la table des fréquences */ fwrite(&nbre_ascii, 2, 1, dst); /*Ecriture de la table des fréquences */ if (frq==NULL) { i=index_frequence; while(i!=-1) { nbre_ascii=i; fwrite(&nbre_ascii, 1, 1, dst); fwrite((char *)&arbre_d[i].frequence, 4, 1, dst); i=arbre_d[i].index_suivant; } } /* Coinstruction de l'arbre à partir de la table des fréquences */ racine_arbre=huffman_creer_arbre(index_frequence); /* Allocation de mémoire pour le dictionnaire */ if ((dico=(struct dictionnaire *)malloc(256*sizeof(struct dictionnaire)))==NULL) { /*free(arbre_d); free(arbre);*/ perror("malloc"); return -1; } /* RAZ du champs taille du dico. Si on utilise une table de fréquences prédéfinie pour la compression, et qu'un caractère à compresser n'est pas présent dans la table, alors il ne sera pas traité */ for(i=0; i<256; i++) dico[i].taille=0; /* Création du dictionnaire à partir de l'arbre */ huffman_creer_dictionnaire(code, racine_arbre, 0); /* Compression du fichier source et écriture dans le fichier cible */ fseek(src, 0, SEEK_SET); code[0]=0; bit_w=0x80; /* Lecture de BUF_LEN octets dans le fichier source */ while ((r=fread(buffer, 1, BUF_LEN, src))>0) { /* Traitement octet par octet */ for(i=0; i<r; i++) { /* Ecriture du code correspondant au caractère dans le dictionnaire */ octet_r=0; bit_r=0x80; /* Ecriture bit par bit */ for(bit_count=0; bit_count<dico[buffer[i]].taille; bit_count++) { if (dico[buffer[i]].code[octet_r] & bit_r) code[0]|=bit_w; /*else code[0]&=~(bit_w); */ bit_r>>=1; if (bit_r==0) { octet_r++; bit_r=0x80; } bit_w>>=1; if (bit_w==0) { /*printf("%3x", code[0]);*/ fputc(code[0], dst); code[0]=0; bit_w=0x80; } } } } if(bit_w!=0x80) fputc(code[0], dst); free(dico); #ifdef LINUX_COMPIL gettimeofday(&tv, NULL); printf("Compactage effectué en %u \xb5s. Taux de compression: %.2f\n", tv.tv_usec-t_debut, (float)nbre_octets/ftell(dst)); #else printf("Compactage terminé. Taux de compression : %.2f\n", (float)nbre_octets/ftell(dst)); #endif return 0; } /* Decompresse le fichier src Le résultat se trouve dans dst */ char huffman_decompacter(FILE *src, FILE *dst, FILE *frq) { int i, j, r; unsigned long nbre_octets; unsigned char nbre_ascii, bit_r; short index_frequence; short racine_arbre; unsigned char buffer[BUF_LEN]; #ifdef LINUX_COMPIL struct timeval tv; unsigned int t_debut; gettimeofday(&tv, NULL); t_debut=tv.tv_usec; #endif /* Lecture de la taille du fichier original */ fread((char *)&nbre_octets, 4, 1, src); printf("Taille du fichier original : %d octets\n", nbre_octets); printf("Décompression en cours...\n"); /* Lecture de la table des fréquences */ if (frq==NULL) { index_frequence=huffman_lire_frequences(src); } else { fread(&nbre_ascii, 1, 1, src); index_frequence=huffman_lire_frequences(frq); } if (index_frequence==-1) { printf("Erreur de lecture de la table des frequences\n"); return -1; } /* Construction de l'arbre à partir de la table des fréquences */ racine_arbre=huffman_creer_arbre(index_frequence); /* Decompression du fichier source et écriture du résultat dans le fichier cible */ j=racine_arbre; /* Lecture de BUF_LEN octets dans le fichier source */ while ((r=fread(buffer, 1, BUF_LEN, src))>0) { /* Traitement octet par octet */ for(i=0; i<r && nbre_octets>0; i++) { /* Traitement bit par bit */ for(bit_r=0x80; bit_r!=0 && nbre_octets>0; bit_r>>=1) { if (buffer[i]&bit_r) j=arbre[j].branche1; else j=arbre[j].branche0; if ((arbre[j].branche0==-1) || (arbre[j].branche1==-1)) { /*printf("%c", j);*/ fputc((char)j, dst); nbre_octets--; j=racine_arbre; } } } } #ifdef LINUX_COMPIL gettimeofday(&tv, NULL); printf("Décompression effectuée en %u\xb5s\n", tv.tv_usec-t_debut); #else printf("Décompression terminée\n"); #endif return 0; } /* Fonction principale Ouvre les fichiers en lecture ou écriture et alloue un espace mémoire suffisant pour les structures */ int main(int argc, char *argv[]) { FILE *src, *dst, *frq; /* Syntaxe incorrecte */ if (argc<4) { printf("%s c/d/f source destination [frequence]\n", argv[0]); return -1; } /* Ouverture du fichier source en lecture */ if ((src=fopen(argv[2], "rb"))==NULL) { perror("fopen"); return -1; } /* Ouverture du fichier cible en écriture */ if ((dst=fopen(argv[3], "wb"))==NULL) { perror("fopen"); return -1; } /* Ouverture d'un éventuel fichier de liste de fréquences, en lecture */ if (argc>4) { if ((frq=fopen(argv[4], "rb"))==NULL) { perror("fopen"); return -1; } } else frq=NULL; /* Allocation mémoire pour les données diverses necessaires à la construction de l'arbre */ if ((arbre_d=(struct arbre_data *)malloc(512*sizeof(struct arbre_data)))==NULL) { perror("malloc"); return -1; } /* Allocation d'une zone mémoire pour l'arbre */ if ((arbre=(struct arbre *)malloc(512*sizeof(struct arbre)))==NULL) { free(arbre_d); perror("malloc"); return -1; } /* Compression ou décompression ? */ if (*argv[1]=='c') huffman_compacter(src, dst, frq); else if (*argv[1]=='d') huffman_decompacter(src, dst, frq); else if (*argv[1]=='f') huffman_creer_fichier_frequences(src, dst); /* Libération de la mémoire */ free(arbre_d); free(arbre); /* Fermeture des fichiers */ fclose(src); fclose(dst); if (frq!=NULL) fclose(frq); #ifdef LINUX_COMPIL printf("Linux rules!\n"); #endif return 0; }