資料結構課程設計-哈夫曼編碼譯碼
阿新 • • 發佈:2019-01-03
//******************************************** //程式功能:哈夫曼編碼及譯碼 // //日期: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); }