Desktop/Daniele/Program Editing/Progetto di informatica 2/componenti/compressore.cpp

00001 
00002 
00003 #include "compressore.h"
00004 #include "listadoppia.h"
00005 
00006 //#define DEBUG true
00007 /*
00008 togliere il commento se si vuole provare il programma in funzione debug
00009 ho utilizzato le direttive di compilazione condizionale per includere parti
00010 di codice che mi sono servite per il debug, a tal proposito ho introdotto la
00011 macro sopra riportata*/
00012 /* funzione che eleva a potenza b un intero a */
00013 int compressore::elevato (int a, int b)
00014 {
00015 if (a == 0) return 0;
00016 int app = 1;
00017 for (int i = 0; i < b; i++)
00018     app *= a;
00019 return app;
00020 }
00021 
00022 /*questa funzione è necessaria per come è implementata la traduzione da
00023 carattere a intero, infatti in certi casi i valori sono completamente
00024 errati, definisco questa funzione per operare direttamente sui bit*/
00025 int compressore::traduci (char c)
00026 {
00027 int a = 0;
00028 for (int i = 0; i < 8; i++)
00029     a = a + ((c >> (i)) & 1) * elevato(2,i);
00030 return a;
00031 }
00032     #if DEBUG
00033     #include <iostream> 
00034     #endif
00035     #if DEBUG
00036     namespace com
00037     {
00038     void preorder (node* tree)
00039     {
00040     if (tree == 0) return;
00041     cout << tree -> c << ' ' << tree -> freq << endl;
00042     preorder (tree -> left);
00043     preorder (tree -> right);
00044     }
00045     void postorder (node* tree)
00046     {
00047     if (tree == 0) return;
00048     postorder (tree -> left);
00049     postorder (tree -> right);
00050     cout << tree -> c << ' ' << tree -> freq << endl;
00051     }
00052     void inorder (node* tree)
00053     {
00054     if (tree == 0) return;
00055     inorder (tree -> left);
00056     cout << tree -> c << ' ' << tree -> freq << endl;
00057     inorder (tree -> right);
00058     }
00059     }
00060     #endif
00061     
00062 //costruttore: compie tutte le operazioni necessarie
00063 compressore::compressore(const char file[], const char fileo[])
00064 {
00065     #if DEBUG
00066     cout << "DEBUG" << endl;
00067     #endif
00068 stream.open (file, ios::in | ios::binary);
00069 if (!stream) throw "apertura";
00070     #if DEBUG
00071     cout << "apertura ultimata" << endl;
00072     system ("PAUSE");
00073     #endif
00074     
00075 contaoccorrenze();
00076     #if DEBUG
00077     cout << "conteggio occorrenze terminato" << endl;
00078     for (int i = 0; i < 256; i++)
00079         cout << char(i) << ' ' << v[i] << endl;
00080     system ("PAUSE");
00081     #endif
00082 
00083 int a = arrayfreq();
00084 num = a;
00085 delete[] v; //cancella v, infatti adesso il vettore è inutile e occupa solo spazio
00086 v = 0;
00087     #if DEBUG
00088     cout << "vettorizzazione, stampo risultati" << endl;
00089     cout << "vettore pf:" << endl;
00090     for (int i = 0; i < a; i++)
00091         cout << pf[i]->c << ' ' << pf[i]->freq << endl;
00092     system ("PAUSE");
00093     #endif
00094 
00095 
00096 IOs out (fileo, pf, a);
00097 heap hp(pf, a); //in questo punto viene generato lo heap
00098     #if DEBUG
00099     cout << "generazione heap completata" << endl;
00100     for (int i = 0; i < a; i++)
00101         cout << hp.v[i] -> c << ' ' << hp.v[i]->freq << endl;
00102     system ("PAUSE");
00103     #endif
00104 
00105 
00106 int last = hp.lastn(); 
00107 root = huffman (hp, last); //costruisce l'albero di huffman
00108 delete[] pf; //una volta che ho distrutto il contenuto di questo array
00109              //anche l'heap corrispondente è vuoto
00110 pf = 0; //dopo il passo sopra questo puntatore è inutile
00111     #if DEBUG
00112     cout << "costruzione albero ultimata" << endl;
00113     com::preorder (root);
00114     cout << "--------" << endl;
00115     com::postorder (root);
00116     cout << "--------" << endl;
00117     com::inorder (root);
00118     system ("PAUSE");
00119     #endif
00120 
00121 c = new char*[256]; //array che conterrà le codifiche per velocizzare
00122 if (c == 0) throw "memoria";
00123 for (int i = 0; i < 256; i++)
00124     c[i] = 0;
00125 char* app = new char[last+1];  //la traduzione
00126 if (app == 0) throw "memoria";
00127 app[0] = '\0';
00128 if (root -> left == 0 && root -> right == 0)
00129    {
00130    int i = traduci(root -> c);
00131    c[i] = new char[2];
00132    if (c[i] == 0) throw "memoria";
00133    c[i][0] = '1';
00134    c[i][1] = '\0';
00135    }
00136 else
00137     linearizza(root, app); //l'albero viene linearizzato nel vettore c
00138                           //che oltre al carattere contiene direttamente la
00139                           //frequenza
00140 delete[] app;
00141 app = 0;
00142 cancella(root);
00143     #if DEBUG
00144     cout << "array di codifica ultimato" << endl;
00145     for (int i = 0; i < a; i++)
00146         cout << c[i].c << ' ' << c[i].codice << endl;
00147     system ("PAUSE");
00148     #endif
00149 
00150 comprimi(file ,out);
00151 for (int i = 0; i < last + 1; i++)
00152     delete[] c[i];
00153 delete[] c; //cancello anche il vettore di codifiche
00154             //ora che la compressione è terminata
00155 
00156 c = 0;
00157 stream.close();
00158 }
00159 
00160 void compressore::cancella(node*& tree) //funzione di appoggio per la
00161                                         //distruzione dell'albero
00162 {
00163 if (tree==0) return;
00164 cancella (tree ->left);
00165 cancella (tree -> right);
00166 delete tree;
00167 tree = 0;
00168 }
00169 
00170 void compressore::contaoccorrenze() //funzione conta le occorrenze di ogni
00171 {                                   //carattere calcolandone la frequenza
00172 v = new int[256];
00173 if (!v) throw "memoria";
00174 for (int i = 0; i < 256; i++)
00175     v[i] = 0;
00176 int a;
00177 a = stream.get();
00178 while (stream)
00179     {
00180     v[a]++;
00181     a = stream.get();
00182     }
00183 }
00184 
00185 /*per la prossima funzione ho pensato di usare la classe listadoppia per evitare
00186 di scorrere più volte l'array e, dato che essa è munita di distruttore
00187 la memoria al termine verrà liberata*/
00188 
00189 int compressore::arrayfreq()
00190 {
00191 listadoppia l;
00192 for (int j = 0; j < 256; j++)
00193     if (v[j] > 0) l.inserisci(j);
00194 int n = l.numero();
00195 if (n == 0) throw "vuoto";
00196 pf = new frequenza* [n];
00197 if (!pf) throw "memoria";
00198 for (int i = 0; i < n; i++)
00199     {
00200     int e = l.estrai();
00201     pf[i] = new frequenza;
00202     if (!pf[i]) throw "memoria";
00203     pf[i] -> c = char(e);
00204     pf[i] -> freq = v[e];
00205     pf[i] -> left = pf[i] -> right = 0; 
00206     }
00207 if (n == 0) throw "vuoto";
00208 return n; //ritorna il numero di caratteri-frequenze trovate nel testo
00209 }
00210 
00211 /*huffman costruisce l'albero di huffman con il suo stesso algoritmo, a partire
00212 da un heap ordinato in modo che la radice abbia frequenza minore, comincia a
00213 estrarre dalla radice due nodi alla volta creando un sottoalbero che viene
00214 reinserito nell'heap, e continua fino a che non ci rimane un solo puntatore
00215 nell'heap, che viene restituito e assegnato alla variabile root della classe
00216 compressore, ora ho la mia codifica*/
00217 
00218 node* compressore::huffman (heap H, int last) //il mio heap va a costruire
00219 {                                             //l'albero di huffman
00220 for(int i = 0; i < last; i++) 
00221     {
00222     node* t = new node;
00223     if (t == 0) throw "memoria";
00224     t->left = H.estrai();
00225     t->right = H.estrai();
00226     t->freq= t->left->freq + t->right->freq; 
00227     H.inserisci(t);     
00228     }
00229 return H.estrai();
00230 }
00231 
00232 void compressore::linearizza (node* tree, char* codice, int j)
00233 {
00234 if (tree == 0) return;                          //funzione che raccoglie le
00235 if (tree -> left == 0 && tree -> right == 0)    //codifiche dall'albero
00236    {
00237    int i = traduci (tree -> c); //memorizzo la codifica direttamente nella
00238                                 //cella corrispondente al carattere (accesso
00239                                 //diretto hash)
00240    c[i] = new char[j+1];
00241    for (int a = 0; a < j+1; a++)
00242        c[i][a] = codice[a];
00243    return;
00244    }
00245 char* codices = new char[j+2];
00246 char* codiced = new char[j+2];
00247 strcpy (codices,codice);
00248 strcpy (codiced,codice);
00249 codices[j] = '0';
00250 codices[j+1] = '\0';
00251 codiced[j] = '1';
00252 codiced[j+1] = '\0';
00253 linearizza(tree->left,codices, j+1);
00254 linearizza(tree->right,codiced, j+1);
00255 delete[] codices;
00256 delete[] codiced;
00257 }
00258 
00259 void compressore::comprimi(const char file[], IOs& out)
00260 {  //la comprimi mentre legge il file scrive nel file specificato alla
00261    //costruzione di compressore la codifica tramite la classe IO
00262 char a;
00263 stream.clear();
00264 stream.close();
00265 stream.open(file, ios::in | ios::binary);
00266 a = char(stream.get());
00267 while (stream)
00268     {
00269     int i = traduci(a);
00270     if (c[i] == 0) throw "lettura";
00271     for (int j = 0; j < strlen(c[i]); j++)
00272         out.inserisci(int(c[i][j]) - int('0'));
00273     a = char(stream.get());
00274     }
00275 if (!stream.eof()) throw "compressione";
00276 out.svuota();
00277 }
00278 

Generated on Sat May 20 14:57:56 2006 for Huffzip by  doxygen 1.4.6-NO