数据结构——赫夫曼编码实现数据压缩与解压原理剖析,附源码(下)

赫夫曼编码解压原理(接上篇)

  • 解压原理解析
    上篇已经分析了赫夫曼编码的压缩原理,相反解压就会清晰许多,无非是拿到压缩后的文件先转成字节数组,然后拿到对应的赫夫曼编码表,对照着表来解压还原字节数组,最后再输出到文件即可完成。
  • 现在要考虑的问题是如何处理压缩后生成的byte数组和对应赫夫曼编码表,首先想到的是将字符数组中的每个字节转化成字符串形式的二进制,这点非常重要,它对应的是赫夫曼编码表里面的每个字符的编码,根据这个字符串形式的二进制,比对着赫夫曼编码表就能还原压缩前的数据
  • 下述方法就是如何将压缩后生成的字符数组根据二进制转成字符串。
/**
	 * byte转二进制字符串
	 * @param flag 判断是否不是最后一个byte 不是:true 是:false
	 * @param b 单个字节
	 * @return
	 */
	private static String byteToBitString(boolean flag,byte b) {
		int temp = b;
		//如果不是最后一个byte
		//正数需要补高位 负数不需要但补也没影响
		if(flag&&b>=0) {
			temp |= 256;
		}
		String str = Integer.toBinaryString(temp);
		//如果是最后一位且字节>=0 直接返回即可
		if(!flag&&b>=0) {
			return str;
		}
		//否则需要截取后八位返回
		return str.substring(str.length()-8);
	}
  • 然后进行数据解压,需要传入压缩后的字节数组与对应赫夫曼编码表。
    将字节数组转化成字符串形式的二进制之后,对应编码表还原数据的真实字节
/**
	 * 数据解压
	 * @param bytes 压缩后的byte数组
	 * @param huffmanCodes 对应赫夫曼编码表
	 * @return
	 */
	public static byte[] huffmanDecod(byte[] bytes,Map<Byte,String> huffmanCodes) {
		StringBuilder sb = new StringBuilder();
		//遍历压缩后的byte[] 逐个将字节转成二进制字符串顺序添加到sb中
		for (int i = 0; i < bytes.length; i++) {
			sb.append(byteToBitString(i!=bytes.length-1,bytes[i]));
		}
		//这一步尤为重要 我们需要根据编码值反向找对应字节(与压缩恰好相反)
		//所以需要反转Map的Key和Value,因为通过Key找Value方便,Value找Key效率极低。
		Map<String,Byte> huffmanCodex = new HashMap<>();
		for(Entry<Byte,String> entry : huffmanCodes.entrySet()) {
			huffmanCodex.put(entry.getValue(), entry.getKey());
		}
		//定义集合listByte ,存储根据编码值还原后的byte
		List<Byte> listByte = new ArrayList<>();
		//定义标志量
		int flag;
		//遍历二进制字符串 这里需要注意 由于赫夫曼编码为不定长编码
		//所以在比对过程中并不确定 每个字节对应的二进制字符串位数
		//所以定义flag标志量,每次从最小一位开始尝试匹配 匹配结果 有且只有一种
		//因为前面章节已经提到 谁也不是谁的前缀 也不是谁的后缀 所以存在且唯一
		for (int i = 0; i < sb.length();) {
			flag = 1;
			while(true) {
				//如果匹配到
				if(huffmanCodex.get(sb.substring(i, i+flag))!=null) {
					//就从二进制字符串sb中截取并get到对应字节存储到集合listByte中
					listByte.add(huffmanCodex.get(sb.substring(i, i+flag)));
					break;
				}
				//如果本轮没有匹配到 就+1继续匹配
				else {
					flag++;
				}
			}
			//每匹配到一个字节,就把它对应的赫夫曼编码长度加到i上
			//目的在于跳过已匹配的编码继续循环
			i += flag;
		}
		//循环结束 所有还原后的字节都存储在了集合listByte中
		byte[] by = new byte[listByte.size()];
		//复制到byte数组中
		for (int i = 0; i < by.length; i++) {
			by[i] = listByte.get(i);
		}
		//返回
		return by;
	}
  • 解压工作基本完成,接下来封装一层,因为实际应用中都是解压目标路径下的文件
/**
	 * 文件解压 将压缩包解压至目标路径
	 * @param srcFile 解压路径
	 * @param dstFile 压缩包路径
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public static void decodFile(String srcFile,String dstFile) throws IOException, ClassNotFoundException {
		//读取
		FileInputStream fis = new FileInputStream(dstFile);
		//嵌套缓冲流
		BufferedInputStream bis = new BufferedInputStream(fis);
		//转对象流
		ObjectInputStream ois = new ObjectInputStream(bis);
		//读入
		Object objBytes = ois.readObject();
		//强转
		byte[] bytesZip = (byte[])objBytes;
		//读入
		Object objHuffmanCodes = ois.readObject();
		//抢转
		Map<Byte,String> huffmanCodes = (Map<Byte,String>)objHuffmanCodes;
		//关键一步 传入压缩后的字节数组与对应编码表解压
		byte[] bytes = huffmanDecod(bytesZip,huffmanCodes);
		
		FileOutputStream fos = new FileOutputStream(srcFile);
		
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		//解压后写出到文件
		bos.write(bytes);
		//资源关闭
		ois.close();
		
		bis.close();
		
		bos.close();
		
	}
  • 测试
    解压G盘Huffman目录下的一个zip
    在这里插入图片描述

测试代码

	//解压测试
	@Test
	public void test2() {
		String srcFile = "G://Huffman/爪哇.zip";
		String zipFile = "G://Huffman/Java.txt";
		try {
			decodFile(srcFile,zipFile);
			System.out.println("解压成功");
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

测试结果
在这里插入图片描述
大小对比
在这里插入图片描述
↓↓↓↓↓↓ 基于赫夫曼编码的数据解压部分完结,下面是前半部分压缩链接 ↓↓↓↓↓↓

数据结构——赫夫曼编码实现数据压缩与解压原理剖析,附源码(上)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值