1. 程式人生 > >資料結構課程設計-哈夫曼編碼譯碼

資料結構課程設計-哈夫曼編碼譯碼

//********************************************
//程式功能:哈夫曼編碼及譯碼
//
//日期:2014年11月18
//
//********************************************
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <windows.h>

#define MAX 128             //葉子節點個數(對應ASCII碼)
#define M 2*MAX-1           //樹的節點個數

typedef struct node{
	int weight;				//權值
	int parent;				//雙親位置
	int LChild;				//左孩子位置
	int RChild;				//右孩子位置
}HTNode,HuffmanTree[M+1];
typedef struct Node{
	char letter;//字元
	char* code;//編碼
	int w;//權值(對應文章中字母(letter)出現次數)
}Huffman[MAX+1];

HuffmanTree ht;					//儲存哈夫曼樹
Huffman qz;						//權值相關資訊
int weight[M+1]={0};			//儲存臨時權值
int t=0;						//儲存葉子節點個數

/*********************函式宣告**********************/
void select(int *s1,int *s2);//建立哈夫曼樹時的選擇權值

int ReadWeight();//從檔案中讀文章獲得權值,返回權值個數

void CrtHuffmanTree();//建立哈夫曼樹

void Encoding();//編碼

void Print();//列印

void WriteTree();//向檔案中寫入哈夫曼樹

void Initialization();//初始化

void WriteCode();//向檔案中寫入編碼

void Decoding();//譯碼

int find_letter(char ch);//查詢字元

int find_code(char s[]);//查詢編碼

void InitTree();//初始化樹

void TreePrinting();//列印哈夫曼樹

void Menu();//選單

void load();//loading

void _print(FILE *fp,node hft,int n);

//主函式
int main()
{
	Menu();//呼叫選單,完成一系列工作
	return 0;
}

void Menu()
{
	char chose;
	load();
	InitTree();
	printf("**************************Menu*************************\n");
	printf("******初始化(I)***********編碼(E)********譯碼(D)*******\n");
	printf("****列印程式碼檔案(P)***列印哈夫曼樹(T)****退出(O)*******\n");
	printf("*******************************************************\n");
	while(true){
		printf("請選擇:");
		scanf("%c",&chose);
		getchar();//除去回車
		switch(chose){
		case 'I':Initialization();break;		//初始化
		case 'E':Encoding();break;				//對葉子節點進行編碼
		case 'D':Decoding();break;				//譯碼
		case 'P':Print();break;					//列印編碼
		case 'T':TreePrinting();break;			//列印哈夫曼樹
		case 'O':
			printf("要退出了哦!\n");			//退出提醒
			Sleep(2000);						//掛起
			exit(1);							//退出程式
		default:printf("怎麼能選錯呢!\n");
		}
	}
}

//loading
void load()
{
	printf("loading");
	for(int i=1;i<=10;i++){
		printf(".");
		Sleep(500);
	}
	printf("\n就要進入啦!\n");
	Sleep(2000);
}
//初始化樹
void InitTree()
{
	ht[0].weight = 0;//標誌哈夫曼樹是否存在
	qz[0].w = 0;	//標誌是否編碼
}
//初始化
void Initialization()
{
	ht[0].weight = 1;		//初始化標誌,說明哈夫曼樹以存在
	t=ReadWeight();			//讀取權值
	CrtHuffmanTree();		//建立哈夫曼樹
	WriteTree();			//將哈夫曼樹寫入檔案
	printf("耶!'初始化'成功啦!\n");
}
//將哈夫曼樹寫入檔案
void WriteTree()
{
	FILE *fp;//hfmTree 檔案指標
	int m=2*t-1;
	int i;

	//開啟檔案
	if((fp=fopen("F:\\hfmTree.txt","w")) == NULL){
		printf("open hfmTree.txt--file error\n");
		exit(0);
	}//else printf("open hfmTree.txt--file sucess!\n");
	//哈夫曼樹寫入檔案
	for(i=1;i<=m;i++)
		fprintf(fp,"%d %d %d %d\n",ht[i].weight,ht[i].parent,ht[i].LChild,ht[i].RChild);
	//關閉檔案
	if(fclose(fp)){
		printf("close hfmTree.txt--file error!\n");
		exit(0);
	}//else printf("close hfmTree.txt--file success!\n");
}
//選擇s1,s2
void select(int n,int *s1,int *s2)
{
	int i;
	int min;
	//尋找一個沒有雙親的節點,找到後退出迴圈
	for(i=1; i<=n; i++){
		if(ht[i].parent == 0){
			min = i;
			break;
		}
	}
	//尋找最小無雙親節點
	for(i=1; i<=n; i++){
		if(ht[i].weight<ht[min].weight && ht[i].parent == 0)
			min = i;
	}
	*s1 = min;
	//尋找次最小無雙親節點
	for(i=1; i<=n; i++){
		if(ht[i].parent == 0 && i != *s1){
			min = i;
			break;
		}
	}
	for(i=1; i<=n; i++){
		if(ht[i].weight < ht[min].weight && i != *s1 && ht[i].parent == 0)
			min = i;
	}
	*s2 = min;

}
//讀取文章,獲取權值
int ReadWeight()//返回權值個數
{
	int n=1;
	FILE *fp;
	char ch;

	//開啟檔案
	if((fp=fopen("F:\\ToBeTran.txt","r")) == NULL){
		printf("Open ToBeTran.txt--file error!\n");
		exit(0);
	}//else printf("open ToBeTran.txt--file sucess!\n");

	//讀取字元
	while(!feof(fp)){//一直迴圈,知道檔案結束
		ch = fgetc(fp);
		if(ch != EOF){
			weight[ch]++;
		}
	}

	//關閉檔案
	if(fclose(fp)){
		printf("close ToBeTran.txt--file error!\n");
		exit(0);
	}//else printf("close ToBeTran.txt--file success!\n");

	//臨時權值轉化到qz[]中
	for(int i=0;i<M;i++){
		//printf("%d ",weight[i]);
		if(weight[i]>=1){
			qz[n].letter = (char)i;
			qz[n].w = weight[i];
			n++;
		}
	}
	return n-1;//n從1開始計數
}

//建立哈夫曼樹
void CrtHuffmanTree()
{
	int i,s1,s2,m = 2*t-1;
	//*初始化*//
	for(i=1; i<=t; i++){//第1到n個位置放置n個葉子節點
		ht[i].weight = qz[i].w;
		ht[i].parent = ht[i].LChild = ht[i].RChild = 0;
	}
	for(i=t+1;i<=m;i++){//第n+1個到第m個位置放置非葉子節點
		ht[i].weight = ht[i].parent = ht[i].LChild = ht[i].RChild = 0;
	}

	//*建立*//
	for(i=t+1; i<=m; i++){
		select(i-1,&s1,&s2);
		//printf("s1 = %d,s2 = %d\n",s1,s2);
		ht[i].weight = ht[s1].weight + ht[s2].weight;
		ht[s1].parent = ht[s2].parent = i;
		ht[i].LChild = s1;
		ht[i].RChild = s2;
	}

}
//哈夫曼編碼
void Encoding()
{
	if(ht[0].weight == 0){
		printf("哇哦!!居然沒初始化!!\n");
		Sleep(2000);//掛起一段時間
		return;
	}
	int i,start,c,p;
	char *cd;
	cd = (char*)malloc(t*sizeof(char));
	cd[t-1]='\0';
	for(i=1; i<=t; i++){//對n個葉子節點進行編碼
		start = t-1;//定位到臨時編碼陣列的最後一位
		c = i;//記錄當前節點位置
		p = ht[i].parent;//記錄當前節點的雙親位置
		while(p!=0){
			--start;
			if(ht[p].LChild == c)//若該節點是其雙親的左孩子,則編碼0
				cd[start]='0';
			else//若為右孩子則編碼1
				cd[start]='1';
			c = p;//下次迴圈的準備條件
			p = ht[p].parent;
		}
		qz[i].code=(char*)malloc((t-start)*sizeof(char));
		strcpy(qz[i].code,&cd[start]);
	}
	free(cd);
	
	//以上程式碼完成編碼工作
	
	/*測試程式碼
	for(i=1;i<=n;i++)
	printf("%c %d %s\n",hc[i].letter,hc[i].w,hc[i].code);*/
	//將編碼寫入檔案
	WriteCode();
	/*for(i=1;i<=n;i++){
	printf("%s\n",hc[i].code);
	}*/
	qz[0].w = 1;//標誌以編碼 
	printf("耶!'編碼'成功啦!\n");
}

//編碼寫入函式
void WriteCode()
{
	FILE *fp_code,*fp_text;
	char ch;
	int i;
	//開啟編碼儲存檔案
	if((fp_code=fopen("F:\\CodeFile.txt","w")) == NULL){
		printf("open CodeFile.txt--file error !\n");
		exit(0);
	}//else printf("open CodeFile.txt--file success!\n");

	//開啟需要編碼的文字檔案
	if((fp_text=fopen("F:\\ToBeTran.txt","r")) == NULL){
		printf("open ToBeTran.txt--file error !\n");
		exit(0);
	}//else printf("open ToBeTran.txt--file success!\n");

	while(!feof(fp_text)){
		ch = fgetc(fp_text);
		//printf("%c ",ch);
		i = find_letter(ch);
		//printf("i = %d\n",i);
		if(i!=0)
			fprintf(fp_code,"%s ",qz[i].code);
	}

	//關閉檔案
	if(fclose(fp_code)){
		printf("close CodeFile.txt--file error!\n");
		exit(0);
	}//else printf("close CodeFile.txt--file success !\n");
	if(fclose(fp_text)){
		printf("close ToBeTran.txt--file error!\n");
		exit(0);
	}//else printf("close ToBeTran.txt--file success !\n");
}

//查詢字元
int find_letter(char ch)
{
	int low,high,i;
	low = 1;high = t;
	//二分查詢
	while(high - low >= 0){
		i=(low+high)/2;
		if(qz[i].letter == ch)
			return i;
		else if(qz[i].letter < ch){
			low = i+1;
		}
		else
			high = i-1;
	}
	return 0;
}
//列印哈夫曼樹的節點權值
void Print()
{
	if(ht[0].weight == 0){
		printf("哇哦!!居然沒初始化!\n");
		Sleep(2000);//掛起一段時間
		return;
	}
	if(qz[0].w == 0){
		printf("哇塞!!居然沒編碼!!\n");
		Sleep(2000);
		return;
	}
	int i=0;
	char code[100];
	FILE *fp_r,*fp_w;
	if((fp_r=fopen("F:\\CodeFile.txt","r")) == NULL){
		printf("open CodeFile.txt--file error!\n");
		exit(0);
	}//else printf("open CodeFile.txt success!\n");
	if((fp_w=fopen("F:\\CodePrint.txt","w")) == NULL){
		printf("open CodePrint.txt--file error!\n");
		exit(0);
	}//else printf("open CodePrint.txt success!\n");
	while(!feof(fp_r)){
		fscanf(fp_r,"%s\n",code);
		printf("%s ",code);
		fprintf(fp_w,"%s",code);
		i++;
		if(i%5 == 0){
			printf("\n");
			fprintf(fp_w,"\n");
		}
	}
	printf("耶!'列印'成功啦!\n");
}

//譯碼
void Decoding()
{
	if(ht[0].weight == 0){
		printf("哇哦!!居然沒初始化!\n");
		Sleep(2000);//掛起一段時間
		return;
	}
	if(qz[0].w == 0){
		printf("哇塞!!居然沒編碼!!\n");
		Sleep(2000);//掛起一段時間
		return;
	}
	char code[100];
	FILE *fp_r,*fp_w;
	int i;
	//開啟CodeFile.txt檔案,從中讀取編碼
	if((fp_r=fopen("F:\\CodeFile.txt","r")) == NULL){
		printf("open CodeFile.txt--file error!\n");
		exit(0);
	}//else printf("open CodeFile.txt success!\n");
	
	//開啟TextFile.txt檔案,儲存翻譯的內容
	if((fp_w=fopen("F:\\TextFile.txt","w")) == NULL){
		printf("open TextFile.txt--file error!\n");
		exit(0);
	}//else printf("open TextFile.txt success!\n");
	
	while(!feof(fp_r)){
		fscanf(fp_r,"%s\n",code);
		i = find_code(code);
		if(i!=0)
			fprintf(fp_w,"%c",qz[i].letter);
	}
	if(fclose(fp_r)){
		printf("close CodeFile.txt--file error!\n");
		exit(0);
	}//else printf("close CodeFile.txt--file success !\n");
	if(fclose(fp_w)){
		printf("close TextFile.txt--file error!\n");
		exit(0);
	}//else printf("close TextFile.txt--file success !\n");
	printf("耶!'譯碼'成功啦!\n");
}

int find_code(char s[])//查詢編碼
{
	int i;
	for(i=1;i<=t;i++){
		if(strcmp(qz[i].code,s) == 0)
			return i;
	}
	return 0;
}

void TreePrinting()
{
	if(ht[0].weight == 0){
		printf("哇哦!!居然沒初始化!\n");
		Sleep(2000);//掛起一段時間
		return;
	}
	FILE *fp;
	int i,r=t*2-1;
	//開啟檔案
	if((fp=fopen("F:\\TreePrint.txt","w")) == NULL){
		printf("open CodeFile.txt--file error !\n");
		exit(0);
	}
	for(i=1;i<=r;i++){
		if(ht[i].parent == 0)
			break;
	}
	//printf("%d\n",ht[i].parent );
	_print(fp,ht[i],0);
	if(fclose(fp)){
		printf("close hfmTree.txt--file error!\n");
		exit(0);
	}
	printf("耶!'列印'成功啦!\n");
}
//哈夫曼樹縱向顯示並寫入檔案
void _print(FILE *fp,node hft,int n)
{
	if(hft.LChild == 0 && hft.RChild == 0){
		for(int i=0;i<n;i++){
			printf(" ");
			fprintf(fp," ");
		}
		printf("%-6d\n",hft.weight);
		fprintf(fp,"%-6d\n",hft.weight);
		return ;
	}
	_print(fp,ht[hft.RChild],n+2);
	for(int i=0;i<n;i++){
		printf(" ");
		fprintf(fp," ");
	}
	printf("%-6d\n",hft.weight);
	fprintf(fp,"%-6d\n",hft.weight);
	_print(fp,ht[hft.LChild],n+2);
}