哈夫曼编码实现文件的压缩和解压

哈夫曼编码的概念

哈夫曼编码是基于哈夫曼树实现的一种文件压缩方式。
哈夫曼树:一种带权路径最短的最优二叉树,每个叶子结点都有它的权值,离根节点越近,权值越小(根节点权值为0,往下随深度增加依次加一),树的带权路径等于各个叶子结点的数值与其权值的乘积和。哈夫曼树如图:
在这里插入图片描述
从图中我们可以看出,数据都存放在叶子结点中,且为了达到树的带权路径最短,我们把数值大的节点放在靠近根的位置,这棵树的带权路径长度为:23+53+72+131=48。接下来我们为每个节点赋予哈夫曼编码,假设从根节点出发,到左子树获得编码0,到右子树获得编码1,这样我们可以得到D的编码是0,B的编码是10,C的编码是110,A的编码是111。离根越近的节点对应的编码越短,节点的数值越大。那么,如何把哈夫曼编码应用在文档的压缩上呢?我们记文件中字符出现的次数为节点的数值,出现次数最多的字符会分配到哈夫曼树的靠近根节点的地方,自然也就会获得较短的哈夫曼编码。于是我们通过这种方式,使得文档中的字符获得不同的哈夫曼编码,因为出现频次高的字符对应编码较短,所以从文档中获取的字节被哈夫曼编码替换之后,会获使得其占用的总存储空间变小,实现压缩的效果。

实现哈夫曼压缩和解压的步骤详解

建立哈夫曼树:
1、使用IO流逐字节读取TXT文档。用一个数组(0~255,下标表示ASCII码)来保存不同字符出现的次数(对应位置加一)。
2、建一个节点类,保存节点对象的信息。将数组每一位表示的字符和出现频次存入创建的节点,把所有节点存入一个链表。
3、根据节点存储的频次值,对链表进行排序(从小到大)。
4、从链表中取出并删除最小的两个节点,创建一个他们的父节点,父节点不存字符,值为那两个节点的和,把那两个节点分别作为其左子节点和右子节点,最后把这个父节点存入链表。再次排序,取出并删除最小的两个节点,生成父节点,再存入…以此类推,最终生成一棵哈夫曼树。
5、对哈夫曼树进行遍历,使得叶子结点获得相应编码,同时把字符和它对应的哈夫曼编码存入HashMap。
哈夫曼压缩的实现:
1、再次读取原文档(之前第一次读取只是为了获取HashMap),根据HashMap中的字符与编码的键值对把整个文档转化为一串01码(此处可以用01字符串表示)。
2、准备将数据写入要压缩的目录。首先把HashMap写入(如果压缩文件中没有HashMap的信息,在解压的时候将无法还原)。HashMap包括两个部分,一部分是key值(即字符),占一个字节,另一部分是01字符串编码,若转为字节表示,可能小于8位有可能大于8位(即长度不确定),我们在写入时必须明确每个01串占据的字节个数,再者,因为我们是以字节的形式写数据,写数据的时候总位数应是8的整数倍,需要对01串末尾补0。我们具体是这样写HashMap的:写键值对的数量(占一个字节);写key值(把字符转为ASCII值写入,占一个字节);写01码占几个字节(是补0后的字节数,此信息占一个字节);写补0情况(某位补0数,此处也占一个字节),写补零后的01码对应的若干字节。继续下一个键值对的写入…以此类推,直到整个HashMap的键值对都写完。
3、刚才写的是编码信息,接下来准备把整个原文档转换得到的01串写入,这也是我们之后需要还原的信息。刚才的流没有关闭,我们是继续写入的。因为这依然会遇到最后一个字节不足8位的情况,我们需要补0并记录补0情况。先写整个文档的补0情况(一个字节),再把补0后的01串以每8位为一个字节写入压缩文件。
4、以上操作便实现了哈夫曼压缩。另外需要注意的是,IO流的read()和write()方法是对字节进行读写,如果写的是int类型的数据,那么它表示的是相应的ASCII码值,如果写入的是字符,也是会转化为对应的字节的(0~255个字符都有对应的ASCII码,也都有对应的字节表示)。
压缩格式如图
在这里插入图片描述
解压的实现:
1、先读取第一个字节,即编码个数,确定了我们需要读多少组数据。
2、开始正式读取键值对信息。读取key值,读取01码对应的字节数,读取补0情况,再读取表示01串的字节数据,去掉之前补的0,还原回0和1表示的字符串,即字符对应的哈夫曼编码,把读到的字符和哈夫曼编码保存在一个新建的HashMap中,需要注意的是此处key值存储为哈夫曼编码,value值存储为字符的信息。以此类推,直到读完所有键值对信息。
3、读整个文件补0个数,读取文件字节数据,去掉补的0,得到之前存入的哈夫曼编码01字符串。
4、确定希望解压的文件目录。逐位读取01字符串,将读到的位累加在一个临时字符串中,每读一位都拿这个临时字符串和HashMap进行对照,如果有对应key值,则获取对应字符信息写入流,把字符串置空,继续循环累加新的01串。最终读完后,解压目录中便得到了我们解压后的文件。

代码实现

1、节点类:

public class Node<T> implements Comparable<Node<T>>{
   
	private T data;
	private int weight;
	private Node<T> left;
	private Node<T> right;	
	public Node(T data,int weight)
	{
   
		this.data=data;
		this.weight=weight;
	}		
	/**
	 * 获取节点数据
	 */
	public String toString()
	{
   
		return "data:"+data+"   "+"weight:"+weight;
	}
	/**
	 * 节点权值比较方法
	 * @param o
	 * @return
	 */
	public int compareTo(Node<T> o) {
          
		if(this.weight>o.weight)
			return 1;
		else if(this.weight<o.weight)
			return -1;
		return 0;
	}	
	public void setData(T data)
	{
   
		this.data=data;
	}	
	public void setWeight(int weight)
	{
   
		this.weight=weight;
	}	
	public T getData()
	{
   
		return data;
	}	
	public int getWeight()
	{
   
		return weight;
	}	
	public void setLeft(Node<T> node)
	{
   
		this.left=node;
	}	
	public void setRight(Node<T> node)
	{
   
		this.right=node;
	}	
	public Node<T> getLeft()
	{
   
		return this.left;
	}	
	public Node<T> getRight()
	{
   
		return this.right;
	}		
}

2、mian方法入口及建树的方法

public class HFMcompression {
   	
	public static void main(String[] args)
	{
   
		HFMcompression hc = new HFMcompression();
		File file  = new File("E:\\workspace\\mayifan\\src\\com\\myf\\HFMcompression1223\\data1.txt");//源文件地址	
		FileOperation fo = new FileOperation();
		int [] a = fo.getArrays(file);		
		System.out.println(Arrays.toString(a)); //打印		
		LinkedList<Node<String>> list = hc.createNodeList(a);//把数组的元素转为节点并存入链表			
		for(int i=0;i<list.size();i++)
		{
   
			System.out.println(list.get(i).toString());
		}
		Node<String> root = hc.CreateHFMTree(list); //建树		
		System.out.println("打印整棵树、、、、");
		hc.inOrder(root); //打印整棵树
		System.out.println("获取叶子结点哈夫曼编码");
		HashMap<String,String> map = hc.getAllCode(root);//获取字符编码HashMap          
		String str = fo.GetStr(map, file);
		System.out.println("转化得到的01字符串:"+str);
	        File fileCompress = new File("E:\\workspace\\mayifan\\src\\com\\myf\\HFMcompression1223\\data2.zip");//压缩文件地址
		fo.compressFile(fileCompress,map,str);  //生成压缩文件
		File fileUncompress = new File("E:\\workspace\\mayifan\\src\\com\\myf\\HFMcompression1223\\data3.txt");//压缩文件地址
		fo.uncompressFile(fileCompress,fileUncompress);//解压文件至fileUncompress处
	}	
	/**
	 * 把获得的数组转化为节点并存在链表中
	 * @param arrays
	 * @return
	 */
	public LinkedList<Node<String>> createNodeList(int[] arrays)
	{
   
		LinkedList<Node<String>> list = new LinkedList<>();
		for(int i=0;i<arrays.length;i++)
		{
   
			if(arrays[i]!=0)
			{
   
				String ch = (<
  • 43
    点赞
  • 309
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
编码是一种无损数据压缩算法,可以用于文件压缩和解压缩。下面我将介绍如何使用Python实现编码文件压缩和解压缩。 1. 哈编码实现文件压缩 首先,我们需要构建哈编码树。可以使用优先队列来实现。具体步骤如下: - 遍历文件,统计每个字符出现的频率。 - 将每个字符和它的频率作为叶子节点插入到优先队列中。 - 从优先队列中取出两个频率最小的节点,将它们作为左右子节点构建一个新节点,并将新节点插入到优先队列中。 - 重复上述步骤,直到队列中只剩下一个节点,这个节点就是哈编码树的根节点。 接着,我们可以使用深度优先遍历来生成每个字符的哈编码。具体步骤如下: - 从根节点开始,如果当前节点是叶子节点,则输出它的字符和哈编码。 - 如果当前节点有左子节点,则在哈编码的末尾添加0,并进入左子节点。 - 如果当前节点有右子节点,则在哈编码的末尾添加1,并进入右子节点。 最后,我们可以使用生成的哈编码压缩文件。具体步骤如下: - 遍历文件,将每个字符替换为它的哈编码。 - 将所有哈编码连接起来,每8个位表示一个字节,将其写入压缩文件中。 - 将哈编码表写入压缩文件中。 下面是实现代码: ```python import heapq import os class HuffmanNode: def __init__(self, char, freq): self.char = char self.freq = freq self.left = None self.right = None def __lt__(self, other): return self.freq < other.freq def build_huffman_tree(freq_dict): heap = [] for char, freq in freq_dict.items(): heapq.heappush(heap, HuffmanNode(char, freq)) while len(heap) > 1: node1 = heapq.heappop(heap) node2 = heapq.heappop(heap) new_node = HuffmanNode(None, node1.freq + node2.freq) new_node.left = node1 new_node.right = node2 heapq.heappush(heap, new_node) return heap[0] def generate_huffman_codes(node, code, code_dict): if node.char is not None: code_dict[node.char] = code return generate_huffman_codes(node.left, code + '0', code_dict) generate_huffman_codes(node.right, code + '1', code_dict) def compress_file(input_file, output_file): # Step 1: Build frequency dictionary freq_dict = {} with open(input_file, 'rb') as f: byte = f.read(1) while byte: if byte in freq_dict: freq_dict[byte] += 1 else: freq_dict[byte] = 1 byte = f.read(1) # Step 2: Build huffman tree root = build_huffman_tree(freq_dict) # Step 3: Generate huffman codes code_dict = {} generate_huffman_codes(root, '', code_dict) # Step 4: Compress input file with open(input_file, 'rb') as f_in, open(output_file, 'wb') as f_out: # Write huffman code table for char, code in code_dict.items(): f_out.write(bytes([len(code)])) f_out.write(bytes([char])) f_out.write(code.encode('utf-8')) # Write compressed data byte = f_in.read(1) bits = '' while byte: bits += code_dict[byte] while len(bits) >= 8: byte_out = int(bits[:8], 2) f_out.write(bytes([byte_out])) bits = bits[8:] byte = f_in.read(1) if len(bits) > 0: byte_out = int(bits + '0' * (8 - len(bits)), 2) f_out.write(bytes([byte_out])) ``` 2. 哈编码实现文件压缩 文件压缩的过程与文件压缩的过程相反。具体步骤如下: - 读取哈编码表,生成字符与哈编码的对应关系。 - 读取压缩文件,将每个字节转换为8位二进制数。 - 遍历二进制数,从哈编码表中查找对应的字符。 - 将解后的字符写入解文件中。 下面是实现代码: ```python def decompress_file(input_file, output_file): # Step 1: Read huffman code table code_dict = {} with open(input_file, 'rb') as f: byte = f.read(1) while byte: code_len = int.from_bytes(byte, byteorder='big') char = f.read(1) code = f.read(code_len).decode('utf-8') code_dict[code] = char byte = f.read(1) # Step 2: Decompress input file with open(input_file, 'rb') as f_in, open(output_file, 'wb') as f_out: # Skip huffman code table f_in.seek(sum([2 + len(code) for code in code_dict.keys()])) # Read compressed data byte = f_in.read(1) bits = '' while byte: bits += bin(int.from_bytes(byte, byteorder='big'))[2:].rjust(8, '0') byte = f_in.read(1) # Write decompressed data while len(bits) > 0: for code, char in code_dict.items(): if bits.startswith(code): f_out.write(char) bits = bits[len(code):] break ``` 综上所述,我们可以使用以上代码实现编码文件压缩和解压缩
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值