📄 codhuff.c
字号:
/* Fichier: codhuff.c Auteur: David Bourgin Date de creation: 7/2/94 Date de derniere mise a jour: 24/7/95 Dessein: Exemple de codage Huffman avec comme donnees a compresser le contenu d'un fichier. Attention: Le compilateur doit etre configure pour une pile d'au moins 20 ko.*/#include <stdio.h>/* Pour les routines fgetc,fputc,fwrite et rewind */#include <memory.h>/* Pour les routines memset,memcpy */#include <malloc.h>/* Pour la routine malloc et free */#include <stdlib.h>/* Pour les routines qsort et exit *//* Codes d'erreur renvoyes a l'appelant */#define NO_ERROR 0#define BAD_FILE_NAME 1#define BAD_ARGUMENT 2#define BAD_MEM_ALLOC 3/* Constantes pratiques */#define FALSE 0#define TRUE 1/* Variables globales */FILE *f_source,*f_dest;typedef struct s_arbre { unsigned int octet; /* Un octet est volontairement code comme un entier non signe pour qu'un noeud fictif puisse depasser la valeur 255 */ unsigned long int poids; struct s_arbre *ptr_gauche, *ptr_droit; } t_arbre,*p_arbre;#define OCTET_ARBRE(ptr_arbre) ((*(ptr_arbre)).octet)#define POIDS_ARBRE(ptr_arbre) ((*(ptr_arbre)).poids)#define PGAUCHE_ARBRE(ptr_arbre) ((*(ptr_arbre)).ptr_gauche)#define PDROIT_ARBRE(ptr_arbre) ((*(ptr_arbre)).ptr_droit)typedef struct { unsigned char bits[32]; unsigned int nb_bits; } t_val_bin;#define BITS_VAL_BIN(val_bin) ((val_bin).bits)#define NB_BITS_VAL_BIN(val_bin) ((val_bin).nb_bits) /* Puisque fgetc=EOF uniquement apres un acces alors statut_octet_stocke vaut TRUE si un octet a ete engrange par fgetc ou FALSE s'il n'y aucun octet valide, deja lu et non traite dans val_octet_stocke */int statut_octet_stocke=FALSE;int val_octet_stocke;/* Pseudo procedures */#define debut_des_donnees() { (void)rewind(f_source); statut_octet_stocke=FALSE; }#define fin_des_donnees() (statut_octet_stocke?FALSE:!(statut_octet_stocke=((val_octet_stocke=fgetc(f_source))!=EOF)))#define lire_octet() (statut_octet_stocke?statut_octet_stocke=FALSE,(unsigned char)val_octet_stocke:(unsigned char)fgetc(f_source))#define ecrire_octet(octet) ((void)fputc((octet),f_dest))unsigned char nb_bits_a_ecrire=0;unsigned int val_a_ecrire=0;void ecrire_val_bin(val_bin)/* Parametres en sortie: Aucun Action: Ecrit dans le fichier de sortie la valeur codee en binaire dans 'val_bin' Erreurs: Une erreur d'entree/sortie peut perturber le deroulement de l'algorithme*/t_val_bin val_bin;{ unsigned char pos_bin, pos_octet; pos_octet=(NB_BITS_VAL_BIN(val_bin)+7) >> 3; pos_bin=NB_BITS_VAL_BIN(val_bin) & 7; if (pos_bin) { pos_octet--; val_a_ecrire=(val_a_ecrire<<pos_bin)|BITS_VAL_BIN(val_bin)[pos_octet]; nb_bits_a_ecrire += pos_bin; if (nb_bits_a_ecrire>=8) { nb_bits_a_ecrire -= 8; ecrire_octet((unsigned char)(val_a_ecrire>>nb_bits_a_ecrire)); val_a_ecrire &= ((1<<nb_bits_a_ecrire)-1); } } while (pos_octet) { pos_octet--; val_a_ecrire=(val_a_ecrire<<8)|BITS_VAL_BIN(val_bin)[pos_octet]; ecrire_octet((unsigned char)(val_a_ecrire>>nb_bits_a_ecrire)); val_a_ecrire &= ((1<<nb_bits_a_ecrire)-1); }}void completer_codage()/* Parametres en sortie: Aucun Action: Complete le dernier octet a inscrire dans le fichier de codes par une serie de bits a 0. Erreurs: Aucune*/{ if (nb_bits_a_ecrire) ecrire_octet(val_a_ecrire << (8-nb_bits_a_ecrire));}void ecrire_en_tete(table_codes)/* Parametres en sortie: Aucun Action: Ecrit l'en-tete dans le flux de codes Erreurs: Aucune*/t_val_bin table_codes[257];{ register unsigned int i,j; t_val_bin val_bin_a_0, val_bin_a_1, val_bin; /* Sert a l'envoi en mode binaire via ecrire_val_bin */ *BITS_VAL_BIN(val_bin_a_0)=0; NB_BITS_VAL_BIN(val_bin_a_0)=1; *BITS_VAL_BIN(val_bin_a_1)=1; NB_BITS_VAL_BIN(val_bin_a_1)=1; for (i=0,j=0;j<=255;j++) if NB_BITS_VAL_BIN(table_codes[j]) i++; /* A partir d'ici i contient le nombre d'octets differents d'occurrences non nulles a coder */ /* Premiere partie de l'en-tete: Specifier les octets qui apparaissent dans la source de codage */ if (i<32) { /* Codage des octets apparus par une serie d'octets */ ecrire_val_bin(val_bin_a_0); NB_BITS_VAL_BIN(val_bin)=5; *BITS_VAL_BIN(val_bin)=(unsigned char)(i-1); ecrire_val_bin(val_bin); NB_BITS_VAL_BIN(val_bin)=8; for (j=0;j<=255;j++) if NB_BITS_VAL_BIN(table_codes[j]) { *BITS_VAL_BIN(val_bin)=(unsigned char)j; ecrire_val_bin(val_bin); } } else { /* Codage des octets apparus par une serie de bits */ ecrire_val_bin(val_bin_a_1); for (j=0;j<=255;j++) if NB_BITS_VAL_BIN(table_codes[j]) ecrire_val_bin(val_bin_a_1); else ecrire_val_bin(val_bin_a_0); } /* Seconde partie de l'en-tete: Specifier le codage des octets (fictifs ou non) qui apparaissent dans la source de codage */ for (i=0;i<=256;i++) if (j=NB_BITS_VAL_BIN(table_codes[i])) { if (j<33) { ecrire_val_bin(val_bin_a_0); NB_BITS_VAL_BIN(val_bin)=5; } else { ecrire_val_bin(val_bin_a_1); NB_BITS_VAL_BIN(val_bin)=8; } *BITS_VAL_BIN(val_bin)=(unsigned char)(j-1); ecrire_val_bin(val_bin); ecrire_val_bin(table_codes[i]); }}int comp_poids_arbre(arbre1,arbre2)/* Parametres en sortie: Renvoie un statut de comparaison Action: Renvoie un nombre negatif, nul ou positif selon que le poids de 'arbre2' est inferieur, egal ou superieur au poids de 'arbre1' Erreurs: Aucune*/p_arbre *arbre1,*arbre2;{ return (POIDS_ARBRE(*arbre2) ^ POIDS_ARBRE(*arbre1))?((POIDS_ARBRE(*arbre2)<POIDS_ARBRE(*arbre1))?-1:1):0;}void supprimer_arbre(arbre)/* Parametres en sortie: Aucun Action: Supprime la memoire allouee a un arbre Erreurs: Aucune si l'arbre a ete construit par allocations dynamiques!*/p_arbre arbre;{ if (arbre!=NULL) { supprimer_arbre(PGAUCHE_ARBRE(arbre)); supprimer_arbre(PDROIT_ARBRE(arbre)); free(arbre); }}p_arbre creer_arbre_codage()/* Parametres en sortie: Renvoie un arbre de codage Action: Genere un arbre de codage de Huffman suivant les donnees issues du flux a compresser Erreurs: Une erreur d'entree/sortie peut perturber le deroulement de l'algorithme*/{ register unsigned int i; p_arbre table_occurrences[257], ptr_arbre_fictif; /* Initialiser le nombre d'occurrences de tous les octets a 0 */ for (i=0;i<=256;i++) { if ((table_occurrences[i]=(p_arbre)malloc(sizeof(t_arbre)))==NULL) { for (;i;i--) free(table_occurrences[i-1]); exit(BAD_MEM_ALLOC); } OCTET_ARBRE(table_occurrences[i])=i; POIDS_ARBRE(table_occurrences[i])=0; PGAUCHE_ARBRE(table_occurrences[i])=NULL; PDROIT_ARBRE(table_occurrences[i])=NULL; } /* Valider les occurrences de table_occurrences en fonction des donnees a compresser */ if (!fin_des_donnees()) { while (!fin_des_donnees()) { i=lire_octet(); POIDS_ARBRE(table_occurrences[i])++; } POIDS_ARBRE(table_occurrences[256])=1; /* Tri de la table des occurrences selon le poids de chaque caractere */ (void)qsort(table_occurrences,257,sizeof(p_arbre),comp_poids_arbre); for (i=256;(i!=0)&&(!POIDS_ARBRE(table_occurrences[i]));i--) free(table_occurrences[i]); i++; /* A partir d'ici i donne le nombre d'octets differents d'occurrence non nulle dans le flux a compresser */ while (i>0) /* Parcourir (i+1)/2 fois la table des occurrences pour relier les noeuds en un unique arbre */ { if ((ptr_arbre_fictif=(p_arbre)malloc(sizeof(t_arbre)))==NULL) { for (i=0;i<=256;i++) supprimer_arbre(table_occurrences[i]); exit(BAD_MEM_ALLOC); } OCTET_ARBRE(ptr_arbre_fictif)=257; POIDS_ARBRE(ptr_arbre_fictif)=POIDS_ARBRE(table_occurrences[--i]); PGAUCHE_ARBRE(ptr_arbre_fictif)=table_occurrences[i]; if (i) { i--; POIDS_ARBRE(ptr_arbre_fictif) += POIDS_ARBRE(table_occurrences[i]); PDROIT_ARBRE(ptr_arbre_fictif)=table_occurrences[i]; } else PDROIT_ARBRE(ptr_arbre_fictif)=NULL; table_occurrences[i]=ptr_arbre_fictif; (void)qsort((char *)table_occurrences,i+1,sizeof(p_arbre),comp_poids_arbre); if (i) /* Reste-t-il un noeud dans la table des occurrences? */ i++; /* Oui, alors tenir compte du noeud fictif */ } } return (*table_occurrences);}void coder_table_codes(arbre,table_codes,val_code)/* Parametres en sortie: Les donnees de 'table_codes' peuvent avoir ete modifiees Action: Enregistre l'arbre de codage sous forme d'une table de codages binaires plus rapides d'acces 'val_code' fournit le codage au noeud courant de l'arbre Erreurs: Aucune*/p_arbre arbre;t_val_bin table_codes[257], *val_code;{ register unsigned int i; t_val_bin tmp_val_code; if (OCTET_ARBRE(arbre)==257) { if (PGAUCHE_ARBRE(arbre)!=NULL) /* Les sous-arbres a gauche debutent par un bit a 1 */ { tmp_val_code = *val_code; for (i=31;i>0;i--) BITS_VAL_BIN(*val_code)[i]=(BITS_VAL_BIN(*val_code)[i] << 1)|(BITS_VAL_BIN(*val_code)[i-1] >> 7); *BITS_VAL_BIN(*val_code)=(*BITS_VAL_BIN(*val_code) << 1) | 1; NB_BITS_VAL_BIN(*val_code)++; coder_table_codes(PGAUCHE_ARBRE(arbre),table_codes,val_code); *val_code = tmp_val_code; } if (PDROIT_ARBRE(arbre)!=NULL) /* Les sous-arbres a droite debutent par un bit a 0 */ { tmp_val_code = *val_code; for (i=31;i>0;i--) BITS_VAL_BIN(*val_code)[i]=(BITS_VAL_BIN(*val_code)[i] << 1)|(BITS_VAL_BIN(*val_code)[i-1] >> 7); *BITS_VAL_BIN(*val_code) <<= 1; NB_BITS_VAL_BIN(*val_code)++; coder_table_codes(PDROIT_ARBRE(arbre),table_codes,val_code); *val_code = tmp_val_code; } } else table_codes[OCTET_ARBRE(arbre)] = *val_code;}void creer_table_codes(arbre,table_codes)/* Parametres en sortie: Les donnees de 'table_codes' peuvent avoir ete modifiees Action: Enregistre l'arbre de codage sous forme d'une table de codages binaires plus rapide d'acces Erreurs: Aucune*/p_arbre arbre;t_val_bin table_codes[257];{ t_val_bin val_code; (void)memset((char *)&val_code,0,sizeof(val_code)); (void)memset((char *)table_codes,0,257*sizeof(*table_codes)); coder_table_codes(arbre,table_codes,&val_code);}void codagehuffman()/* Parametres en sortie: Aucun Action: Compresse suivant la methode de Huffman tous les octets lus par la fonction lire_octet Erreurs: Une erreur d'entree/sortie peut perturber le deroulement de l'algorithme*/{ p_arbre arbre; t_val_bin table_codage[257]; unsigned char octet_lu; if (!fin_des_donnees()) /* Generer un codage uniquement s'il y a des donnees */ { arbre=creer_arbre_codage(); /* Creation de l'arbre binaire le mieux adapte */ creer_table_codes(arbre,table_codage); supprimer_arbre(arbre); /* En deduire les codages binaires dans un tableau pour un acces plus rapide */ ecrire_en_tete(table_codage); /* Inscrire la definition du codage */ debut_des_donnees(); /* Compression a propremement parlee des donnees */ while (!fin_des_donnees()) { octet_lu=lire_octet(); ecrire_val_bin(table_codage[octet_lu]); } ecrire_val_bin(table_codage[256]); /* Code de fin de codage */ completer_codage(); /* Completer ce dernier octet avant fermeture du fichier, si necessaire */ }}void aide()/* Parametres en sortie: Aucun Action: Affiche l'aide du programme et termine son execution Erreurs: Aucune*/{ printf("Cet utilitaire permet de compresser un fichier par la methode de Huffman\n"); printf("telle qu'elle est exposee dans 'La Video et Les Imprimantes sur PC'\n"); printf("\nUsage: codhuff source destination\n"); printf("source: Nom du fichier a compresser\n"); printf("destination: Nom du fichier compresse\n");}int main(argc,argv)/* Parametres en sortie: Renvoie un code d'erreur (0=Aucune) Action: Procedure principale Erreurs: Detectee, traitee et un code d'erreur est renvoye si necessaire*/int argc;char *argv[];{ if (argc!=3) { aide(); exit(BAD_ARGUMENT); } else if ((f_source=fopen(argv[1],"rb"))==NULL) { aide(); exit(BAD_FILE_NAME); } else if ((f_dest=fopen(argv[2],"wb"))==NULL) { aide(); exit(BAD_FILE_NAME); } else { codagehuffman(); fclose(f_source); fclose(f_dest); } printf("Execution de codhuff achevee.\n"); return (NO_ERROR);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -