1. 程式人生 > >哈夫曼編碼壓縮,解壓,壓縮比,編碼表,儲存到檔案

哈夫曼編碼壓縮,解壓,壓縮比,編碼表,儲存到檔案

//mian.c

#include "FunctionReference.h"

int main()
{
	HuffmanTree HT;										//哈夫曼樹

	int sum;											//統計的字元總數
	int n;												//字元的種數
	int remainBit;										//最後一個位元組中剩下的沒有意義的位數
	int charCiphertextLength;							//密文陣列的位元組數(大小)
	double yasuobi;										//壓縮比
	int choose = 1;

	Char *bianma;										//統計字元編碼表
	char *charAll ;										//所有的字元
	unsigned char *yima;								//壓縮後字元的存放位置
	char *decodeString = (char *)malloc(sizeof(char) * 400);//譯碼後存放字元的陣列

	
	//初始化編碼表
	if(!InitChar(&bianma))
		return 0;

	while(choose <= 8 && choose >= 1)
	{
		menu ();
		printf("請輸入您的選擇:  ");
		scanf("%d", &choose);
		Choose(choose);
	
		switch(choose)
		{
			case 1:
				InputSourceInConsole(&charAll);	//輸入字元
				sum = Sum(charAll, &bianma, &n);//統計字元
				HuffmanCoding(&HT, n, &bianma);	//進行哈夫曼編碼
				yima = Compress(charAll, n, HT, &remainBit, &charCiphertextLength);	//此處譯出來的是密文陣列

				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				system("pause");
			
				break;
			case 2: 
				ReadCharInSourceFile(&charAll);	//讀取檔案字元
				sum = Sum(charAll, &bianma, &n);//統計字元
				HuffmanCoding(&HT, n, &bianma);	//進行哈夫曼編碼
				yima = Compress(charAll, n, HT, &remainBit, &charCiphertextLength);	//此處譯出來的是密文陣列
			
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				system("pause");

				break;
			case 3:
				ReadHTInHTCODE_txtFile(&HT, &remainBit, &n);		        //讀取HTCODE.txt檔案
				ReadCiphertextInDataSave_txt(&charCiphertextLength, &yima);	//讀取DataSave.txt檔案
				
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				system("pause");

				break;
			case 4:
				WriteCompressData(HT, remainBit, charCiphertextLength, yima, n);//兩個檔案都寫好了
				printf("\n                                          已經成功儲存! \n");

				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");		
				system("pause");


				break;
			case 5:
				printf("\n");
				InformationFileDecoding(HT, yima, decodeString, n, charCiphertextLength, remainBit);//進行譯碼解碼
				OutputTranslateSource(decodeString);	//輸出原字元
			
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				system("pause");	

				break;
			case 6:
				yasuobi = (double)charCiphertextLength / sum;
				printf( "壓縮比為  : %.2lf%% \n", yasuobi * 100);
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				system("pause");

				break;
			case 7:
				printf("\n");
				print(n, HT); //列印編碼表
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				system("pause");

				break;
			case 8:
				charCiphertextLength = 0.0;
				free(HT);
//				free(yima);
				free(bianma);
				free(charAll);
				if(!InitChar(&bianma))
					return 0;

				printf("\n已清空所有分配的空間並重新完成了編碼的初始化!可以繼續測試新的資料!\n");
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
				system("pause");

				break;
			default :
				exit(0);
		}
	}

	return 0;
}

/*說明
需要儲存的東西
壓縮時:
1.隨便儲存很多待壓縮的字元
2.編碼表(來統計字元並來建立哈夫曼數)
3.建立哈夫曼數,通過編碼表和字元
4.壓縮後的陣列

解壓:
1.哈夫曼樹	HT
2.剩餘的位數remainBit
		//HT和remainBit存放在一個檔案中,可以先讀取remainBit(四個位元組),然後全部是HT的提取,每取一次就計算器加1,就可以得到n
3.字元的種數 n	
4.壓縮後的字元陣列yima  已解決
5.解壓放置的陣列	decodingString 已解決,分配1000個	
6.壓縮後多少個儲存字元charCipherTextLength	已解決

*/

//FunctionReference.h

#ifndef HAFFUMANCODE_C	//如果沒有定義過此巨集,就定義
#define HAFFUMANCODE_C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW -2
*/	//如果用此標頭檔案的地方要用到這些巨集,那麼就業可以再此申明這些巨集,在用的檔案1,與用檔案1的檔案只能有一次定義
typedef int Status;

typedef struct 
{
	char code[10];							//編碼
	char node;								//字元
	unsigned int weight;					//權重
	unsigned int parent, lchild, rchild;	//父結點,左右孩子
} HTNode, *HuffmanTree;						//動態分配陣列儲存哈夫曼樹

typedef struct Char
{
	char code[10];		//壓縮編碼
	char node;			//存放字元
	int weight;			//權重
	struct Char *next;	
}Char;

//建立統計字元的編碼表頭節點
Status InitChar (Char **bianma);

//統計字元種數,和編碼表
int Sum(char zifu[], Char **bianma, int *n);

//列印編碼表
void print(int n, HuffmanTree HT);

//每次選擇其中最小的兩個數
void Select(HuffmanTree HT, int maxBorder, int *minL, int *minR);

//--------哈夫曼樹和哈夫曼編碼的儲存表示---------------
int HuffmanCoding(HuffmanTree *HT,  int n, Char **bianma);

//壓縮(根據輸入字元進行壓縮,結果返回壓縮後的密文陣列)
unsigned char* Compress(char *charAll, int charTypeNumber, HuffmanTree HT, int *remainBit, int *saveFileArrayLength);

//譯碼(按壓縮後的陣列方式進行譯碼)
int InformationFileDecoding(HuffmanTree HT, unsigned char fileCharArr[], char decodeString[], int charTypeNumber, int charLength, int remainBit);

//讀取HTCODE.txt檔案(樹HT和remianBit和節點數n)
Status ReadHTInHTCODE_txtFile(HuffmanTree *HT, int *remainBit, int *n);

//讀取密文資料dataSave.txt(讀取到的是經過壓縮,並且以8位一個字元儲存的壓縮陣列)
Status ReadCiphertextInDataSave_txt(int *charCiphertextLength, unsigned char **yima);

//儲存樹和密文陣列,還有最後一位元組剩下位數
Status WriteCompressData(HuffmanTree HT, int remainBit, int charCiphertextLength, unsigned char *yima, int n);

//從原始檔讀入字元
Status ReadCharInSourceFile(char **charAll);

//經過翻譯後,輸出原文字元
Status OutputTranslateSource(char *decodeString);

//從控制檯輸入字元
Status InputSourceInConsole(char **charAll);

//選擇顯示
void Choose(int choose);

//選單顯示
void menu ();

#endif

//FunctionReference.c

#include "FunctionReference.h"

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW -2

//建立統計字元的編碼表頭節點
Status InitChar (Char **bianma)
{
	Char *head;
	head = (Char *)malloc(sizeof(Char) * 1);
	head->next = NULL;
	*bianma = head;
	return OK;
}

//統計字元種數,和編碼表
int Sum(char zifu[], Char **bianma, int *n)
{ 
	int i = 0;		//字元陣列下標
	int j = 1;		//字元種數
	int sum = 0;    //字元個數
	Char *p = *bianma ;
	Char *z = *bianma ;	//p 的前驅

	while (zifu[i]) 
	{
		sum++;
		p = *bianma;
		while (p)
		{
			z = p;
			if (p->node == zifu[i])	//找到此字元,就把對應字元的權重加1,並跳出
			{  
				p->weight++;		
				break;    
			}
			p = p->next;
		}

		if (!p)	//沒有找到字元,則新增一種新的字元
		{   
			p = (Char *)malloc(sizeof(Char)*1);			
			p->node = zifu[i];
			p->weight=1;
			p->next = NULL;
			z->next = p; 
			j++;
		}
		i++; 
	} 
	*n=(j-1); 
	return sum;
}

//列印編碼表
void print(int n, HuffmanTree HT)
{
	int i;
	for (i = 1; i <= n; i++)
	{
		printf("第 %2d個字元是:  %c ,其編碼是 %-8s: ,其權重是 : %3d \n", i, HT[i].node, HT[i].code,  HT[i].weight);
	}
}

//每次選擇其中最小的兩個數
void Select(HuffmanTree HT, int maxBorder, int *minL, int *minR)
{
	int i, j;
	unsigned int minLastOneW, minLastTwoW;

	i = 1;	//找第一個parent不為0的位置,為最小預設值
	while (i <= maxBorder)
	{
		if (HT[i].parent == 0)
		{
			minLastOneW = HT[i].weight;
			*minL = i;
			break;
		}
		i++;
	}

	//找出最小位置
	for (i = 1; i <= maxBorder; i++)
	{
		if ((HT[i].weight < minLastOneW) && (HT[i].parent == 0))
		{
			*minL = i;
			minLastOneW = HT[i].weight;		//找到要更新
		}
	}

	j = 1;	//保證第二個最小數不和第一個最小數的位置重合且其parent不為0的位置,為第二小的預設值
	while(j <= maxBorder)
	{
		if ((j != *minL) && (HT[j].parent == 0))
		{
			minLastTwoW = HT[j].weight;
			*minR = j;
			break;
		}
		j++;
	}

	//找出第二小位置
	for (j = 1; j <= maxBorder; j++)
	{
		if((HT[j].weight < minLastTwoW) && (HT[j].parent == 0) && (j != *minL))
		{
			*minR = j;
			minLastTwoW = HT[j].weight;
		}
	}
}

//--------哈夫曼樹和哈夫曼編碼的儲存表示---------------
int HuffmanCoding(HuffmanTree *HT,  int n, Char **bianma)
{
	Char *q = (*bianma)->next;	//字元統計編碼表迴圈變數
	HuffmanTree p;				//遍歷迴圈變數
	int s1 = 1, s2 = 2;			//每次權重最小的兩個數的位置
	int start;					//求編碼的開始位置
	int codeC, codeF;			//編碼變數
	char *cd;					//儲存每段編碼的字元
	int m;						//總節點數
	int i;						//迴圈變數

	if (n <= 1) return ERROR;
	m = 2 * n - 1;

	*HT = (HuffmanTree)malloc((m + 1)*sizeof(HTNode));
	p = *HT + 1;

	//初始化前n個葉子節點p = *(HT + 1)
	for (i = 1; i <= n; ++i, ++p)
	{
		p->weight = q->weight;
		q = q->next;	//賦值一位,就要往後移一位
		p->lchild = p->rchild = p->parent = 0;
	}
	
	//初始化後面m-1個分支
	for (; i <= m; ++i, ++p)
	{
		p->weight = 0; 
		p->lchild = p->rchild = p->parent = 0;
	}

	//建立哈夫曼樹
	for (i = n + 1; i <= m; ++i)
	{
		Select(*HT, (i - 1), &s1, &s2);	
		(*HT)[s1].parent = i;				
		(*HT)[s2].parent = i;		//把每次找到的兩個最小下標的父節點置為i
		(*HT)[i].lchild = s1;
		(*HT)[i].rchild = s2;		//把當前位置的兩個孩子分別置為最小的下標
		(*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight ;		
	}

	//葉子到根逆向求每個字元的哈夫曼編碼
	cd = (char *)malloc(n * sizeof(char));	//每種編碼的存放,不超過n個,因為樹的深度不會超過n
	q = (*bianma)->next ;

	cd[n - 1] = '\0';
	for (i = 1; i <= n; i++)
	{
		start = n - 1;
		codeC = i;
		codeF = (*HT)[i].parent; 

		//求每一個節點的編碼
		while (codeF != 0)
		{
			if ((*HT)[codeF].lchild == codeC)
				cd[--start] = '0';
			else
				cd[--start] = '1';
		
			 codeC = codeF;
			 codeF = (*HT)[codeF].parent;
		}

		strcpy((*HT)[i].code, &cd[start]);
		(*HT)[i].code[n-start] = '\0';
	}
	free(cd);

	//把字元統計編碼表中的值複製到HT表中
	q = (*bianma)->next;	//首元結點開始
	for(i = 1; i <= n; i++)
	{
		(*HT)[i].node = q->node;
		q = q->next;
	}

	return OK;
}

//壓縮(根據輸入字元進行壓縮,結果返回壓縮後的密文陣列)
unsigned char* Compress(char *charAll, int charTypeNumber, HuffmanTree HT, int *remainBit, int *saveFileArrayLength)
{
	int k = 0;				//每個字元對應的每個編碼的下表控制	
	int	i = 0;				//每個字元的下標控制
	int j = 1;				//每種字元對應去查表的下標控制
	int array = 0;			//存放8位0/1的字元變數
	int count = 0;			//計數器,統計0/1總的個數
	unsigned char *comArrays = (char *)malloc(sizeof(char) * 1);//先分配一個空間

	//錯誤檢測
	if (charTypeNumber <= 1) return ERROR;

	printf("壓縮後的0/1密文: \n");
	comArrays[0] = '0';
	while (charAll[i])
	{
		for (j = 1; j <= charTypeNumber; j++)
		{
			if (charAll[i] == HT[j].node)
			{	
				printf("%s", HT[j].code);		//直接列印壓縮碼

				//用動態陣列來存
				k = 0;
				while(HT[j].code[k])
				{
					comArrays[array] = comArrays[array] | (HT[j].code[k] - '0');
					count++;					//移一位就計數器加一
					if(count%8 == 0)
					{
						comArrays = ( unsigned char *)realloc(comArrays, sizeof(char) * (count/8 + 1));	//滿了一個就在分配一個
						array++;				//陣列儲存的下標變數
						comArrays[array] = 0;	//每迴圈一次,就要把新的初值歸0
					}
					comArrays[array] = comArrays[array] << 1;	//求一位,則往左移一位
					k++;
				}
			}
		}
		i++;
	}
	printf("\n");

	*remainBit = 8 - (count % 8);	//記錄下未在最後一個字元中佔位置的剩下的0/1個數

	if((count%8) != 0)//此處如果移動的次數不是8的整數,則肯定上面有幾位沒有移動的,所以要在手動左移完剩下的次數
	{
		comArrays[array] = comArrays[array] << (*remainBit - 1);
	}

	comArrays[array + 1] = '\0';		//給壓縮的陣列加一個結束符
	*saveFileArrayLength = array + 1;	//保留儲存陣列的長度

	return comArrays;
}


//譯碼(按壓縮後的陣列方式進行譯碼)
int InformationFileDecoding(HuffmanTree HT, unsigned char fileCharArr[], char decodeString[], int charTypeNumber, int charLength, int remainBit)
{		//fileCharArr[]表示從檔案中讀取的全部字元(此表示的是0/1,decodeString[]表示譯出來的字元儲存陣列,charTypeNumber為節點個數,即字元種樹),posotionInHT表示每次在表中的位置狀態

	int i = 0;									//每個字元換成8位0/1位置控制變數
	int k = 0;									//傳的字元位置的下標控制	
	int j = 0;									//把譯出來的字元存放到陣列的下標控制,並且保持每次執行的位置不變
	unsigned char comString[8];					//每一次8位的0/1的存放地方
	unsigned char moveBitCompare;				//移位控制變數	
	int positionInHT = 2 * charTypeNumber - 1;	//查詢哈夫曼表的下標控制,當每次譯出來一個字元的時候,就把其置換為2*n-1
	int breakFlag = 0;

	//錯誤檢測
	if (charTypeNumber <= 1) return ERROR;

	for(k = 0; k < charLength && breakFlag == 0; k++)
	{
		//把兩個字元,轉換成8位0/1,並存放在comString陣列中
		moveBitCompare = 1 << 7;																	
		for(i = 0; i <= 7; i++)
		{
			comString[i] = ((fileCharArr[k] & moveBitCompare) >> (7 - i));
			moveBitCompare = moveBitCompare >> 1;														
		}
							

		//進行8位字元的譯碼,並把譯出來的字元放在decodeString陣列中
		for(i = 0; i < 8; i++)
		{
			if ((comString[i] & 1) == 0)
				positionInHT = HT[positionInHT].lchild;
			else
				positionInHT = HT[positionInHT].rchild;	

			if (HT[positionInHT].lchild == 0 || HT[positionInHT].rchild == 0)
			{
				decodeString[j] = HT[positionInHT].node;
				j++;

				if(((k == (charLength - 1)) && (i == (8 - remainBit - 1))) || (k == (charLength - 2) && (remainBit == 8) && i == 7) )
				{
					breakFlag = 1;	//如果剩餘的數為8位,即多分配了一個位元組則,要此處判斷直接跳出,而不用再計算最後一個全為0且不用計算的了
					break;			//如果譯出了最後一個字元,就結束(k指示到了最後一個字元,且i是最後一個有意義的0/1)	
				}

				positionInHT = 2 * charTypeNumber - 1;	//每找出一個字元就重新衝根節點開始找
			}
		}
	}

	decodeString[j] = '\0';	//給字元加結束符
	return j;	//返回譯出來的字元個數
}

//讀取HTCODE.txt檔案(樹HT和remianBit和節點數n)
Status ReadHTInHTCODE_txtFile(HuffmanTree *HT, int *remainBit, int *n)
{
	int m;
	int end;
	FILE *fp;

	if ((fp = fopen("HTCODE.txt", "rb")) == NULL)
	{
		printf( "開啟檔案失敗!\n");
		exit(0);
	}
	else
	{
		if(fp)//開啟檔案求位元組長度
		{        
			fseek(fp, 0L, SEEK_END);   
			end = ftell(fp);
			fclose(fp);//此處關閉
		}

		m = (end - 4)/sizeof(HTNode);	//m表示樹中的全部節點
		*n = (m + 1)/2;					//n表示樹中葉子節點數, 得到字元種類n

		printf(" 節點種數 : %d \n", *n);
		printf(" 節點總數 : %d \n", m);

		*HT = (HuffmanTree)malloc((m + 1)*sizeof(HTNode));//分配樹的全部節點空間

		fp = fopen("HTCODE.txt", "rb");
		fread(remainBit, sizeof(int), 1, fp);		//讀出剩餘位數,得到remainBit
		//printf(" remainBitjj : %d \n", remainBit);	
		fread(*HT + 1, sizeof(HTNode), m, fp);		//讀取了整棵樹,並且下標從1開始
	}
	fclose(fp);

	return OK;
}

//讀取密文資料dataSave.txt(讀取到的是經過壓縮,並且以8位一個字元儲存的壓縮陣列)
Status ReadCiphertextInDataSave_txt(int *charCiphertextLength, unsigned char **yima)
{
	int end;	//位元組長度
	FILE *fp;	

	fp = fopen("DataSave.txt", "rb");
	if(fp)
	{        
		fseek(fp, 0L, SEEK_END);   
		end = ftell(fp);
		fclose(fp);
	}

	printf(" 檔案中字元個數  : %ld \n", end);	
	*charCiphertextLength = end;					//得到密文陣列的長度charCiphertextLength

	*yima = (unsigned char *)malloc( sizeof(char) * end);	//分配密文長度大小的字元長度

	if ((fp = fopen("DataSave.txt", "rb")) == NULL)
	{
		printf( "開啟檔案失敗!\n");
		exit(0);
	}
	else
	{
		fread(*yima, 1, *charCiphertextLength, fp);	//從檔案中得到密文陣列yima
	}
	fclose(fp);
	return OK;
}

//儲存樹和密文陣列,還有最後一位元組剩下位數
Status WriteCompressData(HuffmanTree HT, int remainBit, int charCiphertextLength, unsigned char *yima, int n)
{
	FILE *fp;

	//樹HT和剩餘位數remainBit儲存HTCODE.txt檔案中
  	if ((fp = fopen("HTCODE.txt", "wb")) == NULL)
	{
		printf( "開啟檔案失敗!\n");
		exit(0);
	}
	else
	{	
		fwrite(&remainBit, sizeof(int), 1, fp);			//寫一個最後剩餘位數到檔案中
		//printf(" remainBit: %d \n", remainBit); 
		fwrite(HT+1, sizeof(HTNode), (2 * n), fp);	//把整棵樹寫入檔案,下標從1開始
	}
	fclose(fp);

	//密文陣列儲存到dataSave.txt檔案中去
	if ((fp = fopen("dataSave.txt", "wb")) == NULL)
	{
		printf( "開啟檔案失敗!\n");
		exit(0);
	}
	else
	{
		fwrite(yima, 1, charCiphertextLength, fp);
	}
	fclose(fp);
	return OK;

}

//從原始檔讀入字元
Status ReadCharInSourceFile(char **charAll)
{
	int end;
	FILE *fp;

	//從檔案SourceFile.txt中讀入字元
	fp = fopen("SourceFile.txt", "rb");
	if(fp)
	{
		fseek(fp, 0L, SEEK_END);
		end = ftell(fp);
		fclose(fp);
	}
	printf(" 檔案中字元個數  : %ld \n", end);
	*charAll = (char *)malloc( sizeof(char) * (end+1));	//分配檔案在檔案大小個可用字元

	if ((fp = fopen("SourceFile.txt", "rb")) == NULL)
	{
		printf( "開啟檔案失敗!\n");
		exit(0);
	}
	else
	{
		fread(*charAll, 1, end, fp);
		(*charAll)[end] = '\0';
	}
	fclose(fp);

	return OK;
}

//經過翻譯後,輸出原文字元
Status OutputTranslateSource(char *decodeString)
{	
	int i = 0;
	while(decodeString[i])
	{
		printf("%c ", decodeString[i]);
		i++;
	}
	printf("\n");
	return OK;
}

//從控制檯輸入字元
Status InputSourceInConsole(char **charAll)
{
	*charAll = (char *)malloc(sizeof(char)*400);
	printf("請輸入一段英文字元: \n");
	fflush(stdin);
	gets(*charAll);
	
	return OK;
}

//選擇顯示
void Choose(int choose)
{
	switch(choose)
	{
		case 1:
			system("cls");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("                       您選擇了 1 -- 從鍵盤輸入字元進行壓縮                                                 \n");
			break;
		case 2: 
			system("cls");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("                       您選擇了 2 -- 從檔案讀取字元進行壓縮                                                 \n");
			break;
		case 3:
			system("cls");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("                       您選擇了 3 -- 從檔案讀取密文進行解碼                                                   \n");
			break;
		case 4:
			system("cls");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("                       您選擇了 4 -- 儲存壓縮及樹檔案                                                       \n");
			break;
		case 5:
			system("cls");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("                       您選擇了 5 -- 根據譯碼測試輸出原始碼                                                   \n");
			break;
		case 6:
			system("cls");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("                       您選擇了 6 -- 計算壓縮比                                                             \n");
			break;
		case 7:
			system("cls");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("                       您選擇了 7 -- 列印編碼表                                                             \n");
			break;
		case 8:
			system("cls");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("                       您選擇了 8 -- 清空表                                                                   \n");
			break;
		default :
			{
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("                             您選擇了其他 -- 退出程式 , 程式即將退出!                                             \n");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			printf("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
			}
	}
}


//選單顯示
void menu ()
{	
	system("cls");
	printf("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
	printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                          歡  迎  使  用  哈  夫  曼  壓  縮  軟  件	                                      ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                                          主  菜  單                                                        ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                                      ( 請按提示操作 )                                                    ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n");
	printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                操 作 :                                                                                     ┃\n");
	printf("┃                                  1  從鍵盤輸入字元進行壓縮                                                 ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                                  2  從檔案讀取字元進行壓縮                                                 ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                                  3  從檔案讀取密文進行解碼                                                 ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                                  4  儲存壓縮及樹檔案                                                       ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                                  5  根據譯碼測試輸出原始碼                                                   ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                                  6  計算壓縮比                                                             ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                                  7  列印編碼表                                                             ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                                  8  清空表                          (其他鍵退出)                           ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┃                                                                                                            ┃\n");
	printf("┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫\n");
	printf("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");	
	

}



/*
//另外一種譯碼方式

int InformationFiveDecoding(HuffmanTree HT, int n, int temp, unsigned char fileCharArr[], char decodeString[])
{		//fileCharArr[]表示每次從檔案中讀取兩個字元(此表示存了16位0/1,decodeString[]表示譯出來的字元儲存陣列,n為節點個數,即字元種樹),temp表示每次在表中的位置狀態

	int i = 0;		//二個字元換成16位0/1位置控制變數
	int k = 0;		//傳的兩個字元的下標控制
	
	static int j = 0;				//把譯出來的字元存放到陣列的下標控制,並且保持每次執行的位置不變
	char comString[17];				//每一次16位的0/1的存放地方

	//錯誤檢測
	if (n <= 1) return ERROR;

	//把兩個字元,轉換成16位0/1,並存放在comString陣列中
	for(k = 1, i = 15; i >= 0; i--)
	{
		comString[i] = (fileCharArr[k]&1);
		fileCharArr[k] = fileCharArr[k] >> 1;
		if(i%8 == 0) k--;
	}printf("\n");

	//測試0/1的譯碼是否正確
						for(i = 0; i < 16; i++)
						{
							printf("%d | ", comString[i]);
						}
	//測試
						

	//進行16位字元的譯碼,並把譯出來的字元放在decodeString陣列中
	for(i = 0; i < 16; i++)
	{
		if ((comString[i]&1) == 0)
			temp = HT[temp].lchild;
		else
			temp = HT[temp].rchild;	

		if(HT[temp].lchild == 0 || HT[temp].rchild == 0)
		{
			decodeString[j] = HT[temp].node;
			j++;
			temp = 2 * n - 1;
		}
	}
	//測試是否每次能翻譯出來
	for(i = 0; i < j; i++)
	{
		printf("%c  ", decodeString[i]);
	}
	printf("\n");

	return temp;	//通過返回上一次的狀態值來為下一次的開始賦初位置
}


//main中 
	temp = 2 * n - 1;
	i = 0;
	while(yima[i])
	{
		fileCharArr[0] = yima[i];
		fileCharArr[1] = yima[++i];

		temp = InformationFiveDecoding(HT, n, temp, fileCharArr, decodeString);
		i++;
	}
*/

請尊重過別人的知識,如果轉載請註明地址

另外: 如果有錯誤或者需要修改的地方,歡迎大家指出