哈夫曼编码压缩,解压,压缩比,编码表,储存到文件


//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++;
	}
*/


请尊重过别人的知识,如果转载请注明地址

另外: 如果有错误或者需要修改的地方,欢迎大家指出

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值