赫夫曼编码--文件压缩实践

上一篇 赫夫曼编码 详细描述了编码的过程和原理,这里就小小的实践一下。

压缩文件

压缩过程中需要读取待压缩文件,然后读取编码规则(赫夫曼编码表)进而对文件进行编码,完成文件的压缩。

//压缩文件
	public static void zipFile(String src,String dst) throws IOException{
		//创建一个输入流
		InputStream in = new FileInputStream(src);
		//创建一个和输入流指向的文件大小一样的byte数组
		byte[] b = new byte[in.available()];
		//读取文件内容
		in.read(b);
		in.close();
		
		//使用赫夫曼编码对byte数组进行文件编码
		byte[] bytezip = huffmanZip(b);
		
		//文件输出
		OutputStream out = new FileOutputStream(dst);
		//需要写入编码后的bytezip数组和赫夫曼编码表
		ObjectOutputStream oo = new ObjectOutputStream(out);
		//写入bytezip数组
		oo.writeObject(bytezip);
		//写入赫夫曼编码表
		oo.writeObject(huffCodes);
		oo.close();
		out.close();	
	}

解压文件

文件解压时需要根据文件压缩的编码规则进行压缩。

//解压文件
	public static void unzipFile(String src,String dst) throws Exception {
		//创建一个输入流
		InputStream in = new FileInputStream(src);
		
		ObjectInputStream oin = new ObjectInputStream(in);
		//读取bytezip数组
		byte[] byteunzip = (byte[]) oin.readObject();
		//读取编码规则(赫夫曼编码表)
		Map<Byte,String> coderule = (Map<Byte, String>) oin.readObject();
		oin.close();
		in.close();
		
		//解码
		byte[] bytes = decode(coderule,byteunzip);
		
		//创建一个输出流
		OutputStream out = new FileOutputStream(dst);
		//写出数据
		out.write(bytes);
		out.close();
	}

全部代码

Node类:

public class Node implements Comparable<Node>{
	
	Byte data;  //可以为空
	int weight;
	Node left;
	Node right;
	
	public Node(Byte data,int weight) {
		this.data = data;
		this.weight = weight;
	}

	@Override
	public int compareTo(Node o) {
		return o.weight - this.weight;  //倒序(从大到小)
	}

	@Override
	public String toString() {
		//打印内容
		return "Node [data=" + data + ", weight=" + weight + "]";
	}
	
	public int getDate() {
		return this.weight;
	}
}

main:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TestHuffmanCode {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		//文件压缩
//		String src = "1.bmp";
//		String dst = "2.zip";
//		try {
//			zipFile(src, dst);
//		}catch(IOException e) {
//			e.printStackTrace();
//		}
		
		//文件解压
		String src2 = "2.zip";
		String dst2 = "3.bmp";
		try {
			unzipFile(src2, dst2);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	//解压文件
	public static void unzipFile(String src,String dst) throws Exception {
		//创建一个输入流
		InputStream in = new FileInputStream(src);
		
		ObjectInputStream oin = new ObjectInputStream(in);
		//读取bytezip数组
		byte[] byteunzip = (byte[]) oin.readObject();
		//读取编码规则(赫夫曼编码表)
		Map<Byte,String> coderule = (Map<Byte, String>) oin.readObject();
		oin.close();
		in.close();
		
		//解码
		byte[] bytes = decode(coderule,byteunzip);
		
		//创建一个输出流
		OutputStream out = new FileOutputStream(dst);
		//写出数据
		out.write(bytes);
		out.close();
	}
	//压缩文件
	public static void zipFile(String src,String dst) throws IOException{
		//创建一个输入流
		InputStream in = new FileInputStream(src);
		//创建一个和输入流指向的文件大小一样的byte数组
		byte[] b = new byte[in.available()];
		//读取文件内容
		in.read(b);
		in.close();
		
		//使用赫夫曼编码对byte数组进行文件编码
		byte[] bytezip = huffmanZip(b);
		//比较压缩前后的内存消耗
//		System.out.println(b.length);
//		System.out.println(bytezip.length);
		
		//文件输出
		OutputStream out = new FileOutputStream(dst);
		//需要写入编码后的bytezip数组和赫夫曼编码表
		ObjectOutputStream oo = new ObjectOutputStream(out);
		//写入bytezip数组
		oo.writeObject(bytezip);
		//写入赫夫曼编码表
		oo.writeObject(huffCodes);
		oo.close();
		out.close();
		
	}

	//使用指定的赫夫曼编码表进行解码
	private static byte[] decode(Map<Byte, String> huffCodes, byte[] bs) {
		// TODO Auto-generated method stub
		StringBuilder sb = new StringBuilder();
		for(int i = 0;i < bs.length;i++) {
			byte b = bs[i];
			boolean flag = (i == bs.length-1);
			sb.append(byteToBitStr(!flag,b));
		}
		Map<String,Byte> map = new HashMap<>();
		for(Map.Entry<Byte, String> entry:huffCodes.entrySet()) {
			map.put(entry.getValue(), entry.getKey());
		}
		List<Byte> list = new ArrayList<>();
		for(int i = 0;i < sb.length();) {
			int count = 1;
			boolean flag = true;
			Byte b = null;
			while(flag) {
				String key = sb.substring(i, i+count);
				b = map.get(key);  
				if(b == null) {
					count++;
				}else {
					flag = false;
				}
			}
			list.add(b);
			i += count;	
		}
		byte[] bts = new byte[list.size()];
		for(int i = 0;i <bts.length;i++) {
			bts[i] = list.get(i);
		}
		return bts;
	}
	
	//补齐为全部8位的元素
	private static String byteToBitStr(boolean flag,byte b) {
		int temp = b;
		if(flag) {
			temp |= 256;   
		}
		String str = Integer.toBinaryString(temp);
		if(flag) {
			return str.substring(str.length()-8); 
		}else {
			return str;
		}
	}

	//进行赫夫曼编码压缩的方法
	private static byte[] huffmanZip(byte[] bytes) {
		// TODO Auto-generated method stub
		List<Node> nodes = getNodes(bytes);
		Node Tree = createHuffmanTree(nodes);
		Map<Byte,String> huffCodes = getCodes(Tree);
		byte[] c = zip(bytes,huffCodes);  
		return c;
	}
	
	//进行赫夫曼编码
	private static byte[] zip(byte[] bytes, Map<Byte, String> huffCodes) {
		// TODO Auto-generated method stub
		StringBuilder sb = new StringBuilder();
		for(byte b:bytes) {
			sb.append(huffCodes.get(b));
		}
		int len;
		if(sb.length()%8 == 0) {
			len = sb.length()/8;
		}else {
			len = sb.length()/8 + 1;
		}
		byte[] by = new byte[len];
		int index = 0;
		for(int i = 0;i < sb.length();i+=8) {
			String StrByte;
			if(i+8 > sb.length()) {
				StrByte = sb.substring(i);
			}else {
				StrByte = sb.substring(i, i+8);
			}
			byte byt = (byte) Integer.parseInt(StrByte,2);
			by[index] = byt;
			index++;
		}
		
		return by;
	}

	//用于临时存储路径
	static StringBuilder sb = new StringBuilder(); 
	//存储赫夫曼编码表
	static Map<Byte,String> huffCodes = new HashMap<>();   
	//根据赫夫曼树获取赫夫曼编码
	private static Map<Byte, String> getCodes(Node tree) {
		// TODO Auto-generated method stub
		if(tree == null) {
			return null;
		}
		getCodes(tree.left,"0",sb);
		getCodes(tree.right,"1",sb);
		
		return huffCodes;
	}

	private static void getCodes(Node node, String code, StringBuilder sb) {
		// TODO Auto-generated method stub
		StringBuilder sbb = new StringBuilder(sb);  //
		sbb.append(code);
		if(node.data == null) {
			getCodes(node.left,"0",sbb);
			getCodes(node.right,"1",sbb);
		}else {
			huffCodes.put(node.data,sbb.toString());
		}
	}

	//创建赫夫曼树
	private static Node createHuffmanTree(List<Node> nodes) {
		// TODO Auto-generated method stub
		while(nodes.size()>1) {
			Collections.sort(nodes);
			
			Node left = nodes.get(nodes.size()-1);
			Node right = nodes.get(nodes.size()-2); 
			
			Node parent = new Node(null,left.getDate() + right.getDate());
			parent.left = left;
			parent.right = right;
			
			nodes.remove(left);
			nodes.remove(right);
			
			nodes.add(parent);
		}
		return nodes.get(0);
	}

	//把bytes数组转为Node集合
	private static List<Node> getNodes(byte[] bytes) {
		// TODO Auto-generated method stub
		List<Node> nodes = new ArrayList<>();
		Map<Byte,Integer> counts = new HashMap<>();  
		for(byte b:bytes) {
			Integer count = counts.get(b);
			if(count == null) {
				counts.put(b, 1); 
			}else {
				counts.put(b, count+1); 
			}
		}
		for(Map.Entry<Byte, Integer> entry:counts.entrySet()) {
			nodes.add(new Node(entry.getKey(),entry.getValue()));
		}
		return nodes;
	}

}

实验压缩图片
1.bmp
压缩文件结果如下(先压缩后解压):1.BMP 和 3.BMP 图片一样
在这里插入图片描述

发布了9 篇原创文章 · 获赞 5 · 访问量 341

猜你喜欢

转载自blog.csdn.net/qq_42964349/article/details/104350018