Huffman对文件编码和解码

考核实验中的一个,我也认为较为难的一个,其实也不是很难,只是有点复杂,只要分解成多个问题去解决就好了

比如你要知道Huffman是怎样对文件进行编码和解码的

然后需要知道怎么去建Huffman二叉树,建好了Huffman树 然后就是对其进行编码  最后是解码

只要把每个过程弄清楚了,用几天时间(对本科生而言)写出来应该还是有可能的

下面的代码是某位同学写的,经过其本人允许我决定贴到我的博客中供大家学习,但是如果按照源代码来演示的话,你懂得……

期中要编码的txt文件必须放在.cpp源文件同个文件夹下,所以大家在写的过程中要注意。。。

当然,这个代码的漏洞也有很多,欢迎大家来吐槽。。。

/*
功能:Huffman对文件进行编码和解码
时间:2014-11-24
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//结点结构体
typedef struct Node
{
	unsigned char value;  //结点值
	int frequency;   //结点频数
	struct Node *Lchild;  //左儿子
	struct Node *Rchild;  //右儿子
};

//编码字符结构体
typedef struct
{
	unsigned char value;  //字符值
	char code[100];   //字符编码
	int frequency;   // 字符频数
	int codelen;   //字符编码长度
}HuffmanCode;

struct Node *root;  //哈弗曼树根节点

//读取文件统计各个字母出现频率,返回字符种数
//filename为文件名,*node为用于保存数据的数组
int count(char filename[],HuffmanCode *node)
{
	FILE *fp=fopen(filename,"rb");  //打开文件
	unsigned char now;  //读取的当前字符
	int num = 0; //字符种数

	fread(&now, sizeof(unsigned char), 1, fp); //读取字符
	while(!feof(fp)) //未读到文件尾
	{
		int i;
		//重复字符
		for (i = 0; i < num; i++)
		{
			if (now == node[i].value)
			{
				node[i].frequency++;
				break;
			}

		}
		//未重复字符
		if (i==num)
		{
			node[i].value = now;
			node[i].frequency=1;
			num++;
		}
		fread(&now, sizeof(unsigned char), 1, fp); //读取字符
	}
	fclose(fp);
	return num;
}

//构造Huffman树
//*node为用于保存数据的数组,n为字符种数
struct Node* buildHuffman(HuffmanCode *node, int n)
{
	struct Node **arr,*temp; //arr为造数用数组,temp为最终返回的树
	arr = (struct Node**)malloc(n*sizeof(struct Node));

	//初始化数组
	for (int i = 0; i < n; i++)
	{
		arr[i] = (struct Node*)malloc(sizeof(struct Node));
		arr[i]->value = node[i].value;
		arr[i]->frequency = node[i].frequency;
		arr[i]->Lchild = arr[i]->Rchild = NULL;
	}

	//建树
	for (int i = 0; i < n-1; i++)
	{
		int m1=-1, m2;  //最小的两个权值在数组中下标

		//将m1,m2指向头两棵树
		for (int j = 0; j < n; j++)
		{
			if (m1 == -1 && arr[j] != NULL)
			{
				m1 = j;
				continue;
			}
			if (arr[j] != NULL)
			{
				m2 = j;
				break;
			}
		}
		//比较权值大小,找到最小的两个
		for (int j = m2; j < n; j++)
		{
			if (arr[j]!=NULL && arr[j]->frequency < arr[m1]->frequency)
			{
				m2 = m1;
				m1 = j;
			}
			else if (arr[j] != NULL && arr[j]->frequency < arr[m2]->frequency)
				m2 = j;
		}
		//将两个权值最小的构成新树
		temp = (struct Node*)malloc(sizeof(struct Node));
		temp->Lchild = arr[m1];
		temp->Rchild = arr[m2];
		temp->frequency = arr[m1]->frequency + arr[m2]->frequency;
		temp->value = NULL;
		//将新树加入数组
		arr[m1] = temp;
		arr[m2] = NULL;
	}
	free(arr);
	return temp; //最终temp为树根
}
//Huffman编码
//*node为根节点,len为当前编码长度,*arr为用于保存数据的数组,num为字符种数
void codeByHuffman(struct Node *node,int len,HuffmanCode *arr,int num)
{
	static char code[100];  //编码

	//左儿子不为空,编码为0
	if (node->Lchild != NULL)
	{
		code[len] = '0';
		code[len + 1] = '\0';
		codeByHuffman(node->Lchild, len+1,arr,num); //对左儿子调用此函数
	}
	//右儿子不为空,编码为1
	if (node->Rchild != NULL)
	{
		code[len] = '1';
		code[len + 1] = '\0';
		codeByHuffman(node->Rchild, len + 1, arr, num); //对右儿子调用此函数
	}
	//叶节点
	else
	{
		for (int i = 0; i < num; i++)
		{
			//将编码复制给数组里的元素
			if (arr[i].value == node->value)
			{
				strcpy(arr[i].code, code);
				arr[i].codelen = 0;
				for (int j = 0; arr[i].code[j] != '\0'; j++)
					arr[i].codelen++;
				return;
			}
		}
	}
}
//将文件内容转为Huffman编码
//sourceFile为原文件名,*arr为用于保存数据的数组,n为字符种数,aimFile为目标文件名
void writeCode(char sourceFile[],HuffmanCode *arr,int n,char aimFile[])
{
	FILE *fSource, *fAim; //原文件以及目标文件
	unsigned char now;//读取的当前字符
	unsigned char save = 0; //每次保存的一个字节长度
	int len = 0; //每次保存的一个字节已存了多少长度
	int totalLen = 0; //文件编码总长度
	int last;  //最后一次写入时的位数

	//总字节长度
	for (int i = 0; i < n; i++)
		totalLen = totalLen + arr[i].codelen*arr[i].frequency;
	//计算最后一次写入多少位
	last = totalLen % 8;

	fSource = fopen(sourceFile, "rb");
	fAim = fopen(aimFile, "wb");

	//先将huffman编码输进文件,以">"作为标识符 用于译码
	fwrite("->",1,2,fAim);
	fwrite(&n, sizeof(int), 1, fAim);  //写入字符种数
	fwrite(&last, sizeof(int), 1, fAim); //写入最后一次写入的位数
	//写入个字符值和字符频数
	for (int i = 0; i < n; i++)
	{
		fwrite(&arr[i].value, sizeof(unsigned char), 1, fAim);
		fwrite(&arr[i].frequency, sizeof(int), 1, fAim);
	}

	fread(&now, sizeof(unsigned char), 1, fSource); //读取源文件字符

	while (!feof(fSource))
	{
		for (int i = 0; 1; i++)
		{
			if (now == arr[i].value)
			{
				for(int j=0;j<arr[i].codelen;j++)
				{
					//按位操作保存编码
					if(len!=0)
						save=save<<1;
					save=save|(arr[i].code[j]-'0');
					len++;
					//一个字节已存满,写入文件
					if(len==8)
					{
						fwrite(&save,sizeof(unsigned char),1,fAim);
						save=0;
						len=0;
					}
				}
				break;
			}
		}
		fread(&now, sizeof(unsigned char), 1, fSource); //读取字符
	}
	//最后一次可能没写满一个字节也要写入文件
	if(len!=0)
		fwrite(&save,sizeof(unsigned char),1,fAim);
	fclose(fSource);
	fclose(fAim);
}
//译码
//sourceFile为原文件名,*arr为用于保存数据的数组,aimFile为目标文件名
void writeDecode(char sourceFile[], HuffmanCode *arr, char aimFile[])
{
	FILE *fSource, *fAim; //原文件以及目标文件
	int n; //字符种数
	int last=0; //最后一次读取的位数
	unsigned char now;//读取的当前字符

	fSource = fopen(sourceFile, "rb");

	//确认是否以此代码编码的Huffman
	for (int i = 0; i < 2;i++)
	{
		fread(&now, sizeof(char), 1, fSource); //读取字符
		if (i == 0)
		{
			if (now == '-')
				continue;
			else
			{
				printf("原文件huffman编码格式不正确\n");
				fclose(fSource);
				return;
			}
		}
		if (now == '>')
			break;
		else
		{
			printf("原文件huffman编码格式不正确\n");
			fclose(fSource);
			return;
		}
	}
	//
	fread(&n, sizeof(int), 1, fSource);	//字符种数
	fread(&last, sizeof(int), 1, fSource); //最后一次读的位数
	for (int i = 0; i < n; i++)
	{
		fread(&arr[i].value, sizeof(unsigned char), 1, fSource);  //字符
		fread(&arr[i].frequency, sizeof(int), 1, fSource);//字符频数
	}

	struct Node *root = buildHuffman(arr, n); //建树
	codeByHuffman(root, 0, arr, n);
	struct Node *pNow = root;  //当前结点
	unsigned char temp; //每次读1个字节
	fAim = fopen(aimFile, "wb");

	fread(&temp, sizeof(unsigned char), 1, fSource);
	while (!feof(fSource))
	{
		unsigned char ifLast;  //用于判断是否读到文件末尾,方便读取固定位数
		fread(&ifLast, sizeof(unsigned char), 1, fSource);
		int i;
		if (feof(fSource))
			i = last-1;
		else
			i = 7;
		for (; i >= 0; i--)
		{
			if ((temp>>i & 1)==0)
				pNow = pNow->Lchild;
			else
				pNow = pNow->Rchild;
			if (pNow->Lchild == NULL && pNow->Rchild == NULL)
			{
				fwrite(&pNow->value, sizeof(unsigned char), 1, fAim);
				pNow = root;
			}
		}
		temp=ifLast;
	}
	fclose(fSource);
	fclose(fAim);
}

void main()
{
	while (1)
	{
		HuffmanCode node[2000];
		int num;  //字符种数
		char Sfilename[100] = ""; //原文件名
		char Afilename[100] = "";//输出文件名
		char choose[3] = "";

		printf("请选择:\n");
		printf("\t1.编码\n");
		printf("\t2.译码\n");
		printf("\t3.退出\n");

		scanf("%s", choose);
		fflush(stdin);
		if (!strcmp(choose, "1"))
		{
			while (1)
			{
				printf("请输入你想要编码的文件名:\n");
				scanf("%s", Sfilename);
				fflush(stdin);
				FILE *fp = fopen(Sfilename, "rb");
				if (fp == NULL)
				{
					printf("未找到文件.\n");
					continue;
				}
				else
				{
					fclose(fp);
					break;
				}
			}
				printf("请输入编码后的文件名:\n");
				scanf("%s", Afilename);
				fflush(stdin);
				num = count(Sfilename, node); //统计
				root = buildHuffman(node, num); //建树

				codeByHuffman(root, 0, node, num);//各个字符的Huffman编码

				writeCode(Sfilename, node, num, Afilename); //输出Huffman编码文件
				printf("编码完成!\n\n");
		}
		else if (!strcmp(choose, "2"))
		{
			while (1)
			{
				printf("请输入你想要译码的文件名:\n");
				scanf("%s", Sfilename);
				fflush(stdin);
				FILE *fp = fopen(Sfilename, "rb");
				if (fp == NULL)
				{
					printf("未找到文件.\n");
					continue;
				}
				else
				{
					fclose(fp);
					break;
				}
			}
			printf("请输入译码后的文件名:\n");
			scanf("%s", Afilename);
			fflush(stdin);
			writeDecode(Sfilename, node, Afilename); //译码并写文件
			printf("译码完成!\n\n");
		}
		else if (!strcmp(choose, "3"))
			return;
		else
		{
			printf("输入有误!\n");
			continue;
		}
	}
}
时间: 2024-10-03 21:55:56

Huffman对文件编码和解码的相关文章

Python学习笔记八:文件操作(续),文件编码与解码,函数,递归,函数式编程介绍,高阶函数

文件操作(续) 获得文件句柄位置,f.tell(),从0开始,按字符数计数 f.read(5),读取5个字符 返回文件句柄到某位置,f.seek(0) 文件在编辑过程中改变编码,f.detech() 获取文件编码,f.encoding() 获取文件在内存中的编号,f.fileno() 获取文件终端类型(tty.打印机等),f.isatty() 获取文件名,f.name() 判断文件句柄是否可移动(tty等不可移动),f.seekable() 判断文件是否可读,f.readable() 判断文件是

java利用Base64编码和解码图片文件

1.编码与解码代码如下所示: import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import javax.image

Huffman编码与解码的实现

Huffman编码相信学过数据结构这么课的都知道,概念也比较好理解,但是一般好理解的算法,在实际实现的过程中总是会遇到各种问题,一方面个人认为是对算法的实现过程不熟,另一方面在实际实现的过程中可以提升自己实现算法的能力,将自己的想法实现后还是比较满足的.下面是本人亲自实现的Huffman编码与解码的C语言实现,主要是记录一下自己当时的想法,供以后备忘吧. 数据结构定义如下: typedef struct{ unsigned int weight; unsigned int parent,lchi

python第三天学习复习,集合set,文件操作,函数(普通函数,递归,高阶函数),字符编码和解码

三元运算 age = 23 #就是if else的简单写法 a = age if age < 20 else 25 集合 set #集合是无序切不重复的, #当对列表去重复的时候,可以直接使用 set(list),就将list转为set,并去除中间重复的 list = [1,2,3,4,5,5,6,7,8,9,1] s = set(list) 运行结果:可以发现将 list中重复的去掉,并且类型变成set,再使用list(set),转为list 集合操作 # Author:zylong set1

20172303 2018-2019-1《程序设计与数据结构》哈夫曼树编码与解码

20172303 2018-2019-1<程序设计与数据结构>哈夫曼树编码与解码 哈夫曼树简介 定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近. 带权路径长度(Weighted Path Length of Tree,简记为WPL) 结点的权:在一些应用中,赋予树中结点的一个有某种意义的实数. 结点的带权路径长度:结点到树根之间的路径长度与

Huffman树与编码

带权路径最小的二叉树称为最优二叉树或Huffman(哈夫曼树). Huffman树的构造 将节点的权值存入数组中,由数组开始构造Huffman树.初始化指针数组,指针指向含有权值的孤立节点. b = malloc(n*sizeof(BTreeNode)); for (i = 0; i < n; i++) { b[i] = malloc(sizeof(BTreeNode)); b[i]->data = a[i]; b[i]->left = NULL; b[i]->right = NU

【转】C/C++ URL编码,解码(提取自PHP)

//解码 //头文件自己加上的 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <string> #include <iostream> using namespace std; /* {{{ php_htoi  */ static int php_htoi(char *s) { int value; int c; c = ((unsigned char *)

http协议中的编码和解码

http://www.csdn1 2 3.com/html/itweb/20130730/29422_29378_29408.htm ****************************** 一.字符集与文字编码简介 1. 计算机如何显示文字 我们知道,计算机是以二进制的“形式”来保存和处理数据的,也 就是说,不管我们使用键盘进行输入,还是让计算机去读取一个文本文件,计算机得到的原始内容是一些二进制序列,当需要对这些二进制序列进行显示时,计算机 会依照某种“翻译机制”(也就是编码方式),取到

在LoadRunner中进行Base64的编码和解码

<Base64 Encode/Decode for LoadRunner>这篇文章介绍了如何在LoadRunner中对字符串进行Base64的编码和解码: http://ptfrontline.wordpress.com/2009/09/30/base64-encodedecode-for-loadrunner/ 在头文件中封装b64_encode_string和b64_decode_string函数: /*Base 64 Encode and Decode functions for Loa