哈夫曼樹之文件壓縮解壓

數據結構課程設計時寫的


//  常量定義 
 
/////////////////////////////
#ifndef G_C                //
#define G_C                //
#define N 1000             // 
#define MAX 99999          //
#endif                     //
/////////////////////////////



//  統計文件中各個字符時相關結構體

///////////////////////////////////////////////////////////
#ifndef W_C						 //
#define W_C						 //
struct Word_Count{					 //
	char word  ;       //字符類型 		         //
	int  count ;       //字符出現次數 	         //
}; 					  		 // 
struct Word_Count_Array{				 // 
	struct Word_Count wC[N];			 // 
	int point;       //定位指針(指向數組wC中第一個空位) // 
};							 // 
#endif						         // 
/////////////////////////////////////////////////////////// 


//  構建Huffuman樹相關結構體


/////////////////////////////////////////////////////////////   
#ifndef H_T						   // 
#define H_T						   // 
struct Node{                  //構建哈夫曼樹的節點           // 
    char word;  					   // 
    int count;					           // 
    struct Node *leftChild,*rightChild;		           // 
    int myAdrr,lfAdrr,rgAdrr; //自己|左孩子|右孩子  Adrress // 
};			      //便於文件中樹的生成          // 
struct Node_Array{                                         // 
	struct Node node[N];                               // 
	int point;                                         // 
};                                                         //
struct Dynamic_Array{         //動態數組                    //
	int dArray[N];                                     //
	int point;                //指向數組中第一個空位     //
};                                                         //
struct WordCode{              //存儲字符以及其編             //
	char word;                //字符                    //
	int code[N];              //Huffu編碼               //
	int point;                //指向數組中第一個空位      //
};                                                          //
struct WordCode_Array{        //存儲字符以及其編碼            // 
	struct WordCode wCode[N];                           //
	int point;                //指向數組中wCode第一個空位 // 
};                                                          //
struct Dynamic_Array  dyArr;  //輔組存儲編碼的動態數組        //
struct WordCode_Array wCArr;  //存儲字符及其編碼              //
int	   Pos=0;                                            //
#endif                                                      //
///////////////////////////////////////////////////////////// 
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *                         		   	         *       
 *  Function Module : Statistical character frequency    * 
 *		        			         *
 *  Explain :         統計字符頻率(Hz),需要緩衝數組buff.   *
 *		        			         *
 *  @author: luewang                  		   	 *	
 *                                 			 *       
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include"GlobalVCS.h"



/**
 *
 * Role:    compare word to count
 *		    
 * Explain: 將傳入的字符與存儲字符的結構體數組
 *	     	中的字符進行比較,更新字符出現次數
 *  	    (Hz)
 *
 * @return: 無 
 *
 */
void OperateWC(struct Word_Count_Array* wCA,char word){
	int i = 0;
	for(; i < wCA->point ; i++){
		if(word==wCA->wC[i].word){
			wCA->wC[i].count+=1;
			break;
		}
	}
	if(i==wCA->point){
		wCA->wC[i].word=word;
		wCA->wC[i].count=1;
		wCA->point=i+1;
	}
}



/**
 *
 * Role:    count word 
 *		    
 * Explain: 統計各個字符出現的頻率(Hz) 
 *
 * @return: 字符頻率結構體數組wCA 
 *
 */
struct Word_Count_Array SingleChar_Hz(char buff[],int n){
	struct Word_Count_Array wCA;         //存儲各個字符出現頻數的結構體數組 *
	wCA.point=0;
	for(int i = 0 ; i < n ; i++){
		OperateWC(&wCA,buff[i]);
	} 
	
	//控制檯打印統計好的字符出現的頻率 
	printf("+----------+-----------+\n");
	printf("+ wCA:字符頻率統計數組 +\n");
	printf("+----------+-----------+\n");
	printf("|   word   |   count   |\n");
	printf("+----------+-----------+\n");
	for(int i = 0 ; i < wCA.point; i++){
		printf("|%10c|%11d|\n",wCA.wC[i].word,wCA.wC[i].count);
		printf("+----------+-----------+\n");
	}
	
	printf("...Over.....OK...\n\n");


	return wCA;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *                         	           			     *       
 *  Function Module (core code) : Establishment Of Huffuman Tree.    * 
 *		     		               			     *
 *  Explain :                              			     *
 *	使用從文件中統計的字符頻率的結構體數組,循環先序遍歷構建Huffu-     *
 *      -man樹,並另外使用結構體數組統計各個字符哈夫曼編碼。              *  
 *                                    				     *
 *  @author: luewang                          			     *
 *                                    				     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include"GlobalVCS.h"



/**
 *
 * Role:    Sort of Insert
 *		
 * Explain: 利用插入排序將統計好個字符頻率的結構體數組wCA 
 *			進行升序排序.
 *
 * @return: 返回後續構建樹所需要的節點的數組 
 *
 */
struct Node_Array InsertSort(struct Word_Count_Array *wCA){
   int  i,j;
   int  tmpCount;
   char tmpChar;
   
   for(i = 1 ; i < wCA->point ; i++){
	 tmpCount = wCA->wC[i].count;
	 tmpChar  = wCA->wC[i].word ;
	for(j = i ; j > 0 && wCA->wC[j-1].count > tmpCount ; j--){
	 	wCA->wC[j].count = wCA->wC[j-1].count;
	 	wCA->wC[j].word  = wCA->wC[j-1].word ;
	}  
 	wCA->wC[j].count = tmpCount;
 	wCA->wC[j].word  = tmpChar ;
   }
   
//  printf("\n\n-----------sort----wCA---------------------\n");
//	for(int k = 0 ; k < wCA->point; k++){
//		printf("wCA : %c---%d \n",wCA->wC[k].word,wCA->wC[k].count);
//	}
	
	struct Node_Array nodeArray;
	for(int i = 0 ; i < wCA->point ; i++){
		nodeArray.node[i].count = wCA->wC[i].count;
		nodeArray.node[i].word  = wCA->wC[i].word ;
		nodeArray.node[i].leftChild = NULL;
		nodeArray.node[i].rightChild= NULL; 
	}
	nodeArray.point = wCA->point;
	
//	printf("\n\n-----------sort----nodeArray---------------------\n");
//	for(int i = 0 ; i < nodeArray.point ; i++){
//		printf("nodeArray : %c----%d \n",nodeArray.node[i].word,nodeArray.node[i].count);
//	} 

	return nodeArray;
} 



/**
 *
 * Role:    Sort of the specified interval
 *		                                                | 
 * Explain: 利用插入排序指定範圍,將構建哈夫曼樹所需要的結 
 *			點的數組進行升序排序.(該函數將會被多次調用) 
 *
 * @return: 無 
 *
 */
void InsertSortSE(struct Node_Array *nA,int start , int end){
   if(end - start > 0){
   	   	int  i,j;
    	int  tmpCount;
   	    char tmpChar;
   	    struct Node *tmpLeft,*tmpRight;
	   	for(i = start + 1 ; i <= end ; i++){
		 tmpCount = nA->node[i].count;
		 tmpChar  = nA->node[i].word ;
		 tmpLeft  = nA->node[i].leftChild;
		 tmpRight = nA->node[i].rightChild;
		for(j = i ; j > start && nA->node[j-1].count > tmpCount ; j--){
		 	nA->node[j].count = nA->node[j-1].count;
		 	nA->node[j].word  = nA->node[j-1].word ;
		 	nA->node[j].leftChild = nA->node[j-1].leftChild;
		 	nA->node[j].rightChild = nA->node[j-1].rightChild;
		}  
	 	nA->node[j].count = tmpCount;
	 	nA->node[j].word  = tmpChar ;
	 	nA->node[j].leftChild = tmpLeft ;
	 	nA->node[j].rightChild= tmpRight;
	   }
   }
}



/**
 *
 * Role:    Copy Array 
 *		                                                
 * Explain: 將數組a的指定範圍(start~end)賦值給數組b該範圍 
 *
 * @return: 無 
 *
 */
void ArrayCopy(int a[],int b[],int start,int end){
	if(end >= 0 && start >= 0 && end - start >= 0){
		for(int i = start ; i <= end ; i++){
			b[i] = a[i];
		}
	}
}



/**
 *
 * Role:    Traverse 
 *		                                                
 * Explain: 利用先序遍歷構建哈夫曼樹(根:Root) 並
 *			將遍歷的結果放在全局變量wCArr(保存字
 *			符及其編碼的數組) 
 *
 * @return: 無 
 *
 */
void Traverse(struct Node* Root){
	if(Root->leftChild==NULL&&Root->rightChild==NULL){  //已經到了根節點 
		Root->myAdrr=Pos;//
		Root->rgAdrr=0;//
		Root->lfAdrr=0;//
		Pos+=1;//
	
		wCArr.wCode[wCArr.point].word = Root->word;
		ArrayCopy(dyArr.dArray,wCArr.wCode[wCArr.point].code,0,dyArr.point-1);
		wCArr.wCode[wCArr.point].point = dyArr.point;
		wCArr.point+=1;
		dyArr.point-=1;
	
		return;
	}
	 
	Root->myAdrr=Pos;//
	Pos+=1;//
	
	if(Root->leftChild != NULL){
		dyArr.dArray[dyArr.point] = 0;
		dyArr.point+=1;
		Root->lfAdrr=Pos;//
		Traverse(Root->leftChild);
	}
	if(Root->rightChild != NULL){
		dyArr.dArray[dyArr.point] = 1;
		dyArr.point+=1;
		Root->rgAdrr=Pos;//
		Traverse(Root->rightChild);
	}
	
	dyArr.point-=1;
	
	return ;
} 



/**
 *
 * Role:    Traverse 
 *		                                                
 * Explain: 純粹先序遍歷樹 
 *
 * @return: 無 
 *
 */
void TreeT(struct Node* Root){
	if(Root->leftChild==NULL&&Root->rightChild==NULL){  //已經到了根節點 
		printf("|%10c|%11d|\n",Root->word,Root->count);	
		printf("+----------+-----------+\n");			
		return ;
	}
	
	printf("|%10c|%11d|\n",Root->word,Root->count);	
	printf("+----------+-----------+\n");		
	
	TreeT(Root->leftChild);
	TreeT(Root->rightChild);
}
 
void Traverset(struct Node* Root){
	
	printf("Tree visting is working......\n");
	
	printf("+----------+-----------+\n");
	printf("|   Visit     Tree     |\n");
	printf("+----------+-----------+\n");
	printf("|   word   |   count   |\n");
	printf("+----------+-----------+\n");
	
	TreeT(Root);
	
	printf("...Over.....OK...\n\n");
}

 

/**
 *
 * Role:    Make a Huffuman Tree
 *		                                                
 * Explain: 根據存儲字符頻率的結構體數組wCA排序轉化
 *			爲創建哈夫曼樹所需要結點數組nodeArray,利
 *			用結點數組進行多次排序並且動態增加元素構
 *			建哈夫曼樹 
 *
 * @return: Root(Huffuman Tree Root) 
 *
 */
struct Node* HuffumanTreeBuild(struct Word_Count_Array *wCA){
	struct Node_Array nodeArray = InsertSort(wCA);//對統計好的字符頻率結構體數組進行排序並將排序放入nodeArray數組中 
	 
	printf(">>>Building Huffuman tree is working......\n");
	 
	struct Node *Root;
	for(int i = 1 ; i < nodeArray.point ; i+=2){
		nodeArray.node[nodeArray.point].count = nodeArray.node[i].count + nodeArray.node[i-1].count;
		nodeArray.node[nodeArray.point].leftChild = &(nodeArray.node[i-1]);
		nodeArray.node[nodeArray.point].rightChild= &(nodeArray.node[i])  ;
		nodeArray.node[nodeArray.point].word='~';
		
		Root = &(nodeArray.node[nodeArray.point]);
		
		nodeArray.point +=1;
		InsertSortSE(&nodeArray,i+1,nodeArray.point-1);
		if(i+1 == nodeArray.point||i+1 == nodeArray.point - 1){
			break;
		}
	}
	
	printf("...Over.....OK...\n\n");
	
	printf("Encoding is working......\n");
	
	Traverse(Root);        //遍歷過程中將每個字符類型code編碼存儲存入全局變量wCArr中 
	
	printf("...Over.....OK...\n\n");
	
	return Root;
}




/**
 *
 * Role:    Show Huffuman Code 
 *
 * Explain: 顯示(存放在全局變量wCA中的)字符編碼
 *		 
 * @return: 無
 * 
 */
void ShowHuffumanCode(){
	printf("+----------+---------------------------+\n");
	printf("+     Huffuman     Code                +\n");
	printf("+----------+---------------------------+\n");
	printf("|   word   |   code                    |\n");
	printf("+----------+---------------------------+\n");
	
	for(int i = 0 ; i < wCArr.point ; i++){
		char str = wCArr.wCode[i].word;
		
		printf("|%-10c|",str);
		
		int j;
		for(j = 0 ; j < wCArr.wCode[i].point ; j++){
			printf("%d ",wCArr.wCode[i].code[j]);
		}
		
		for(int k = 0 ; k < 27-2*j ; k++){
			printf(" ");
		}
		printf("|\n");
		printf("+--------------------------------------+\n");
		
	}
	
	printf("...Over.....OK...\n\n");
	
}
/* * * * * * * * * * * * * * * * * * * * * * * *
 *                         		       *       
 *  Function Module : < > File Operation       * 
 *		        		       *
 *  Explain :    文件操作模塊                   *
 *		        	               *
 *  @author: luewang                  	       *
 *                                 	       *       
 * * * * * * * * * * * * * * * * * * * * * * * */
 #include"GlobalVCS.h"



/**
 *
 * Role:    read word from src to buff
 *		
 * Explain: 從文件中讀入字符並將其存入buff數組中 
 *
 * @return: buff 
 *
 */
char* FileInputStream(char* src){
	FILE *file;
	char buff[MAX];
	
	if((file = fopen(src,"r"))==NULL){
		printf("Error : Open File %s is Faile To Open!!! \n",src);
	}
	
	printf(">>>Open File directory is : %s \n",src);
	
	int ch , count;
	printf("\n-+-+-+-+-+-+-+Content-+-+-+-+-+-+-+-\n");    //顯示從文件中讀出的內容 
	for(count = 0;(ch=fgetc(file))!=EOF;count++){
		buff[count] = ch;
		printf("%c",ch);
	}
	printf("\n-+-+-+-+-+-+-+-End+-+-+-+-+-+-+-+-+-+-\n");
	printf("...Over.....OK...\n\n");
	fclose(file); 
	return buff;
}



/**
 *
 * Role:    file visit and save tree
 *		
 * Explain: 先序遍歷保存構建好的哈夫曼樹 
 *
 * @return: 無 
 *
 */
void SaveTreeVisit(struct Node *Root,FILE *file){
	if(Root->leftChild==NULL&&Root->rightChild==NULL){   //葉子節點 
		
		printf("|%10c|%11d|%12d|%12d|%12d|\n",Root->word,Root->count,Root->myAdrr
								,Root->lfAdrr,Root->rightChild);
		printf("+----------+-----------+------------+------------+------------+\n");
		
		fwrite(Root,sizeof(struct Node),1,file);
		
		return;
	}
	
	printf("|%10c|%11d|%12d|%12d|%12d|\n",Root->word,Root->count,Root->myAdrr
									,Root->lfAdrr,Root->rightChild);
	printf("+----------+-----------+------------+------------+------------+\n");
	
	fwrite(Root,sizeof(struct Node),1,file);
	SaveTreeVisit(Root->leftChild,file);
	SaveTreeVisit(Root->rightChild,file);
}



/**
 *
 * Role:    save huffuman tree to file 
 * 
 * Explain: 將根節點是Root的樹保存到dir路徑文件中. 
 *
 * @return : 無 
 *		
 */
void SaveHuffumanTree(char *dir,struct Node *Root){
	FILE *file;
	
	if((file = fopen(dir,"wb+")) == NULL){
		printf("Error : Save File %s is Faile To Open!!! \n",dir);
	}
	printf(">>>Save File directory is : %s \n",dir);
	
	printf("+----------+-----------+------------+------------+------------+\n");
	printf("|   word   |   count   |   myAdrr   |   lfAdrr   |   rgAdrr   |\n");
	printf("+----------+-----------+------------+------------+------------+\n");
	
	SaveTreeVisit(Root,file);
	
	printf("...Over.....OK...\n\n");
	
	fclose(file);
}


/**
 *
 * Role:    File Visit And Get Tree
 * 
 * Explain: 從文件中取出哈夫曼樹節點信息放入tmpRoot數組中
 *			通過tmpRoot數組中構建哈夫曼樹 , tmpRoot數組每
 *			個元素存儲了左右孩子和自己的地址(int) 
 *
 * @return : tmpRoot頭結點(哈夫曼樹根結點) 
 *		
 */
struct Node* GetTreeVisit(FILE *file){
	int point = 0;
	struct Node tmpRoot[N];
	while(fread(&tmpRoot[point],sizeof(struct Node),1,file)!=NULL){
		
		if(tmpRoot[point].lfAdrr==0&&tmpRoot[point].rgAdrr==0){
			printf("|%10c|%11d|\n",tmpRoot[point].word,tmpRoot[point].count);
			printf("+----------+-----------+\n");
		}
		
		point+=1;
	}
	
	for(int i = 0 ; i < point ; i++){
		if(tmpRoot[i].lfAdrr == 0){
			tmpRoot[i].leftChild = NULL;
		}else if(tmpRoot[i].lfAdrr != 0){
			for(int j = i + 1 ; j < point ; j++){
				if(tmpRoot[j].myAdrr == tmpRoot[i].lfAdrr){
					tmpRoot[i].leftChild = &tmpRoot[j];
					break;
				}
			}
		}
		if(tmpRoot[i].rgAdrr == 0){
			tmpRoot[i].rightChild = NULL;
		}else if(tmpRoot[i].rgAdrr != 0){
			for(int j = i + 1 ; j < point ; j++){
				if(tmpRoot[j].myAdrr == tmpRoot[i].rgAdrr){
					tmpRoot[i].rightChild = &tmpRoot[j];
					break;
				}
			}
		}
	}
	
//	printf("=-=-=-=-=-=\n");
//	for(int i = 0 ; i < point ; i++){
//		printf("%c  \n",tmpRoot[i].word);
//		printf("%d----%d---%d\n",tmpRoot[i].myAdrr,tmpRoot[i].lfAdrr,tmpRoot[i].rgAdrr);
//	}
//	Traverset(tmpRoot);

	return tmpRoot;
}



/**
 *
 * Role:    Get Huffuman Tree
 * 
 * Explain: 從指定(*.dat)文件中獲取獲取哈夫曼樹,給該樹
 *			根結點Root賦值 . 
 *
 * @return : 無 
 *		
 */
void GetHuffumanTree(char *dir,struct Node **Root){ 
	FILE *file;
	
	file = fopen(dir,"rb+");
	if(file==NULL){
		printf("Error : Get File Open is failed To Open : %s\n",dir);
	}
	printf(">>>Get File directory is : %s \n",dir);
	
	printf("+----------+-----------+\n");
	printf("|   word   |   count   |\n");
	printf("+----------+-----------+\n");
	
	*Root = GetTreeVisit(file);      //哈夫曼樹根結點 
	
	printf("...Over.....OK...\n\n");
	
	fclose(file);
}


/**
 *
 * Role:    word zip
 * 
 * Explain: 將指定字符ch根據哈夫曼樹編碼(wCArr)進行壓縮至 
 *			文件 huffuFile 中.
 *
 * @return : 無 
 *		
 */
void ZIP(int ch,FILE *huffuFile){
	for(int i = 0 ; i < wCArr.point ; i++){
		if(ch == wCArr.wCode[i].word){
			for(int j = 0 ; j < wCArr.wCode[i].point ; j++){
				
				printf("%d",wCArr.wCode[i].code[j]);
				
				fputc(wCArr.wCode[i].code[j]+48,huffuFile);
			}
		}
	}
}



/**
 *
 * Role:    file zip
 * 
 * Explain: 指定文件路徑src進行文件壓縮至一個新的文
 *			件中huffuFile 
 *
 * @return : 無 
 *		
 */
void FileZip(char* src){
	FILE *file,*huffuFile;
	
	if((file      = fopen(src,"r+")) == NULL){
		printf("Error : File %s Open is failed!!!\n",src);
	}
	printf(">>>ZIP File directory is : %s \n",src);
	
	char dir[strlen(src)+5];
	strcpy(dir,src);
	dir[strlen(dir)-4]='\0';
	strcat(dir,"_hf.txt");
//	printf("%s\n",dir);
	
	if((huffuFile = fopen(dir,"w+")) == NULL){
		printf("Error : Zipped File %s Open is failed!!!\n",dir);
	}
	printf(">>>Zipped File directory is : %s \n",dir);
	
	int ch; 
	
	printf("\n-+-+-+-fake Binary l stream-+-+-+-+-\n");
	while((ch=fgetc(file))!=EOF){
		ZIP(ch,huffuFile);
	}
	printf("\n-+-+-+-+-+-+-+-End+-+-+-+-+-+-+-+-+-\n");
	
	printf("...Over.....OK...\n\n");
	
	fclose(file);
	fclose(huffuFile);
}



/**
 *
 * Role:    file unzip
 * 
 * Explain: 將指定哈夫曼 0 1 文件(huffuFile)進行哈夫曼樹解
 *			碼存入file文件中 
 *
 * @return : 無 
 *		
 */ 
void UnZIP(struct Node *Root,FILE *huffuFile,FILE *file){
	struct Node *root = Root;   //哈夫曼樹根結點 
	int ch;
	while((ch=fgetc(huffuFile))!=EOF){
//		printf("%c ",ch);

		if(ch - 48 == 0){
			root=root->leftChild;
		}else if(ch - 48 == 1){
			root=root->rightChild;
		}
		
		if(root->rightChild==NULL&&root->leftChild==NULL){  //已經到了葉節點 
			fputc(root->word,file);
			root=Root;                                      //回到根節點 
		}
	}

}



/**
 *
 * Role:    file unzip
 * 
 * Explain: 對給定文件路徑進行哈夫曼樹解壓 Root:
 *			爲哈夫曼樹根結點 
 *
 * @return : 無 
 *		
 */ 
void FileUnZip(char* dir,struct Node *Root){
	//printf("File UnZip\n");	
	//Traverset(Root);

	FILE *file,*huffuFile;
	
	if((huffuFile = fopen(dir,"r+")) == NULL){
		printf("Unzip File Open is failed : %s\n",dir);
	} 
	printf(">>>Unzip File directory is : %s \n",dir);
	
	char Dir[strlen(dir)];
	strcpy(Dir,dir);
	Dir[strlen(dir)-7]='\0';
	strcat(Dir,"`.txt");
	
	//printf("%s\n",Dir);
	
	if((file= fopen(Dir,"w+")) == NULL){
		printf("Unzipped File Open is failed : %s\n",Dir);
	} 
	printf(">>>Unzipped File directory is : %s \n",Dir);
	
	UnZIP(Root,huffuFile,file);
	
	printf("...Over.....OK...\n\n");

	fclose(file);
	fclose(huffuFile);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章