利用赫夫曼编码对文件进行压缩和解压(Java实现)

至于赫夫曼编码是什么我就不多介绍了,直接讲解如何压缩和解压吧

这种方法对大部分文件都适用,并且是无损压缩

1.压缩文件

我写的压缩文件主要分为一下几步
1.将文件转换成字节数组,并转换成Node节点存入List中
2.创建赫夫曼树
3.生成赫夫曼编码
4.根据生成的赫夫曼编码,压缩得到压缩后的赫夫曼编码字节数组

/**
	 * 压缩方法
	 * @param bytes 原始字符串得字节数组
	 * @return 根据生成得赫夫曼编码,压缩得到压缩后得赫夫曼编码字节数组
	 */
	private static byte[] huffmanZip(byte[] bytes){
		List<Node> nodes=getNodes(bytes);
		//创建赫夫曼树
		Node huffmanTreeRoot=createHuffmanTree(nodes);
		//生成对应的赫夫曼编码
		Map<Byte,String> huffmanCodes=getCodes(huffmanTreeRoot);
		//根据生成得赫夫曼编码,压缩得到压缩后得赫夫曼编码字节数组
		byte[] huffmanCodeBytes=zip(bytes,huffmanCodes);
		return huffmanCodeBytes;
	}
	/**
	 * 
	 * @param bytes 原始的字符串对应的字节数组
	 * @param huffmanCodes 生成赫夫曼编码map
	 * @return 返回处理后的byte[]
	 * 举例:原始字符串得到的赫夫曼编码是一长串01字符串,8位对应一个byte ,放入到huffmanCodeBytes
	 * 如:huffmanCodeBytes[0]=10101000(补码),减1取反得到原码11011000=-88
	 * 
	 */
	private static byte[] zip(byte[] bytes,Map<Byte,String> huffmanCodes){
		StringBuilder stringBuilder=new StringBuilder();
		//遍历bytes数组,得到编码
		for(byte b : bytes){
			stringBuilder.append(huffmanCodes.get(b));
		}
		
		//得到huffmanCodeBytes长度,8位一截
		int len=(stringBuilder.length()+7)/8;
		
		byte[] huffmanCodeBytes=new byte[len];
		
		int index=0;
		for(int i=0;i<stringBuilder.length();i=i+8){
			String strByte;
			if(i+8>stringBuilder.length()){
				strByte=stringBuilder.substring(i);
			}else{
				strByte=stringBuilder.substring(i,i+8);
			}
			//将strByte转换成byte放入到huffmanCodeBytes中
			huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte, 2);
			index++;
		}
		return huffmanCodeBytes;
		
	}
	
	
	
	
	/**
	 * 功能:将传入的node节点的所以叶子节点得到其赫夫曼编码,并放入huffmancodes集合中
	 * @param node 传入节点
	 * @param code 路径:左子节点为0,右子节点为1
	 * @param stringbuilder 用于拼接路径
	 */
	private static void getCodes(Node node,String code,StringBuilder stringbuilder){
		StringBuilder stringbuilder2=new StringBuilder(stringbuilder);
		//将code加到stringbuilder2中
		stringbuilder2.append(code);
		if(node!=null){
			if(node.data==null){
				//向左递归
				getCodes(node.left,"0",stringbuilder2);
				//向右递归
				getCodes(node.right,"1",stringbuilder2);
			}else{
				//找到叶子节点
				huffmanCodes.put(node.data, stringbuilder2.toString());
			}
		}
		
	}
	
	//为调用方便,重载getCodes方法
	private static Map<Byte,String> getCodes(Node root){
		if(root==null){
			return null;
		}
		//处理左子树
		getCodes(root.left,"0",stringbuilder);
		//处理右子树
		getCodes(root.right,"1",stringbuilder);
		return huffmanCodes;
		
	}
	
	
	
	/**
	 * 
	 * @param bytes 接受字节数组
	 * @return 返回List节点 如[Node[data=97,value=5]]
	 */
	private static List<Node> getNodes(byte[] bytes){
		ArrayList<Node> nodes = new ArrayList<Node>();
		//遍历bytes,统计每一个字节出现的次数
		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);
			}
		}
		
		//遍历Map,将每一个键值对转换成一个Node对象,保存在List中
		for(Map.Entry<Byte, Integer> entry:counts.entrySet()){
			nodes.add(new Node(entry.getKey(),entry.getValue()));
		}
		return nodes;
	} 

2.解压文件

1.将压缩得到的字节编码转换成二进制字符串
2.通过赫夫曼编码得到原始的字节数组

/**
	 * 解压方法
	 * @param huffmanCodes赫夫曼编码表
	 * @param huffmanBytes赫夫曼编码得到得字节数组
	 * @return返回原本得字符串
	 */
	private static byte[] decode(Map<Byte,String> huffmanCodes,byte[] huffmanBytes){
		StringBuilder strBuilder=new StringBuilder();
		//将byte数组转换成二进制得字符串
		for(int i=0;i<huffmanBytes.length;i++){
			byte b=huffmanBytes[i];
			boolean flag=(i==huffmanBytes.length-1);
			stringbuilder.append(byteToBitString(!flag,b));
		}
		
		//解码
		//将赫夫曼编码进行调换
		Map<String,Byte> map=new HashMap<String,Byte>();
		for(Map.Entry<Byte, String> entry : huffmanCodes.entrySet()){
			map.put(entry.getValue(), entry.getKey());
		}
		
		//创建集合 存放byte
		List<Byte> list = new ArrayList<>();
		for(int i=0;i<stringbuilder.length();){
			int count=1;
			boolean flag=true;
			Byte b=null;
			while(flag){
				String key=stringbuilder.substring(i,i+count);
				b=map.get(key);
				if(b==null){
					count++;
				}else{
					flag=false;
				}
			}
			list.add(b);
			i+=count;
		}
		//将list放入byte[]中并返回
		byte[] b=new byte[list.size()];
		for(int i=0;i<b.length;i++){
			b[i]=list.get(i);
		}
		return b;
	}
	
	
	/**
	 * 将一个byte转换成一个二进制得字符串
	 * @param flag 正数需要补高位为true,flase不需要补高位
	 * @param b 压缩后的byte
	 * @return
	 */
	private static String byteToBitString(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;
		}
	}

3.文件处理

压缩和解压直接调用上面的方法就行

//编写一个方法对文件进行解压
	public static void unZipFile(String zipFile,String dstFile){
		//定义文件输入流
		InputStream is=null;
		//定义一个对象输入流
		ObjectInputStream ois=null;
		//定义文件输入流
		OutputStream os=null;
		
	
		try {
			is=new FileInputStream(zipFile);
			ois=new ObjectInputStream(is);
			//获取字节数组
			byte[] huffmanBytes=(byte[])ois.readObject();
			//获取赫夫曼编码
			Map<Byte,String> buffmanCodes=(Map<Byte,String>)ois.readObject();
			
			//解码
			byte[] bytes=decode(buffmanCodes,huffmanBytes);
			os=new FileOutputStream(dstFile);
			os.write(bytes);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally{
			try {
				os.close();
				ois.close();
				is.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
	
	
	//编写一个方法对文件进行压缩
	/**
	 * 
	 * @param srcFile原始文件
	 * @param dstFile压缩后的文件
	 */
	public static void zipFile(String srcFile,String dstFile){
		OutputStream os=null;
		ObjectOutputStream oos=null;
		FileInputStream is=null;
		
		try {
			is=new FileInputStream(srcFile);
			//创建一个和源文件大小一样的byte[]
			byte[] b=new byte[is.available()];
			//读取文件
			is.read(b);
			//直接对源文件压缩
			byte[] huffmanBytes=huffmanZip(b);
			//创建一个文件输出流,存放压缩文件
			os=new FileOutputStream(dstFile);
			//创建一个和文件输出流关联的ObjectOutputStream
			oos=new ObjectOutputStream(os);
			//吧编码字节数组写入
			oos.writeObject(huffmanBytes);
			//吧赫夫曼编码写入
			oos.writeObject(huffmanCodes);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				is.close();
				oos.close();
				os.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}

4.主方法

	//将生成的赫夫曼编码{32=01,97=100.....}存入huffmanCodes中
	static Map<Byte,String> huffmanCodes=new HashMap<Byte,String>();
	//将生成的赫夫曼编码拼接到一起
	static StringBuilder stringbuilder=new StringBuilder();

	public static void main(String[] args) {
//		String content="i like like like java do you like a java";
//		byte[] contentBytes=content.getBytes();
//	    byte[] b=decode(huffmanCodes,huffmanZip(contentBytes));
//	    System.out.println(new String(b));
		String srcFile="D://11111.jpeg";
		String dstFile="D://Uninstall2.zip";
		String jieFile="D://Uninstall3.jpeg";
		zipFile(srcFile,dstFile);
		System.out.println("压缩成功");
		unZipFile(dstFile,jieFile);
		System.out.println("解压成功");
	}

5.为了方便查看,奉上全部代码

package huffmantree;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
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.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HuffmanTree {
	
	//将生成的赫夫曼编码{32=01,97=100.....}存入huffmanCodes中
	static Map<Byte,String> huffmanCodes=new HashMap<Byte,String>();
	//将生成的赫夫曼编码拼接到一起
	static StringBuilder stringbuilder=new StringBuilder();

	public static void main(String[] args) {
//		String content="i like like like java do you like a java";
//		byte[] contentBytes=content.getBytes();
//	    byte[] b=decode(huffmanCodes,huffmanZip(contentBytes));
//	    System.out.println(new String(b));
		String srcFile="D://11111.jpeg";
		String dstFile="D://Uninstall2.zip";
		String jieFile="D://Uninstall3.jpeg";
		zipFile(srcFile,dstFile);
		System.out.println("压缩成功");
		unZipFile(dstFile,jieFile);
		System.out.println("解压成功");
	}
	
	
	
	//编写一个方法对文件进行解压
	public static void unZipFile(String zipFile,String dstFile){
		//定义文件输入流
		InputStream is=null;
		//定义一个对象输入流
		ObjectInputStream ois=null;
		//定义文件输入流
		OutputStream os=null;
		
	
		try {
			is=new FileInputStream(zipFile);
			ois=new ObjectInputStream(is);
			//获取字节数组
			byte[] huffmanBytes=(byte[])ois.readObject();
			//获取赫夫曼编码
			Map<Byte,String> buffmanCodes=(Map<Byte,String>)ois.readObject();
			
			//解码
			byte[] bytes=decode(buffmanCodes,huffmanBytes);
			os=new FileOutputStream(dstFile);
			os.write(bytes);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally{
			try {
				os.close();
				ois.close();
				is.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
	
	
	//编写一个方法对文件进行压缩
	/**
	 * 
	 * @param srcFile原始文件
	 * @param dstFile压缩后的文件
	 */
	public static void zipFile(String srcFile,String dstFile){
		OutputStream os=null;
		ObjectOutputStream oos=null;
		FileInputStream is=null;
		
		try {
			is=new FileInputStream(srcFile);
			//创建一个和源文件大小一样的byte[]
			byte[] b=new byte[is.available()];
			//读取文件
			is.read(b);
			//直接对源文件压缩
			byte[] huffmanBytes=huffmanZip(b);
			//创建一个文件输出流,存放压缩文件
			os=new FileOutputStream(dstFile);
			//创建一个和文件输出流关联的ObjectOutputStream
			oos=new ObjectOutputStream(os);
			//吧编码字节数组写入
			oos.writeObject(huffmanBytes);
			//吧赫夫曼编码写入
			oos.writeObject(huffmanCodes);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				is.close();
				oos.close();
				os.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}
	
	/**
	 * 解压方法
	 * @param huffmanCodes赫夫曼编码表
	 * @param huffmanBytes赫夫曼编码得到得字节数组
	 * @return返回原本得字符串
	 */
	private static byte[] decode(Map<Byte,String> huffmanCodes,byte[] huffmanBytes){
		StringBuilder strBuilder=new StringBuilder();
		//将byte数组转换成二进制得字符串
		for(int i=0;i<huffmanBytes.length;i++){
			byte b=huffmanBytes[i];
			boolean flag=(i==huffmanBytes.length-1);
			stringbuilder.append(byteToBitString(!flag,b));
		}
		
		//解码
		//将赫夫曼编码进行调换
		Map<String,Byte> map=new HashMap<String,Byte>();
		for(Map.Entry<Byte, String> entry : huffmanCodes.entrySet()){
			map.put(entry.getValue(), entry.getKey());
		}
		
		//创建集合 存放byte
		List<Byte> list = new ArrayList<>();
		for(int i=0;i<stringbuilder.length();){
			int count=1;
			boolean flag=true;
			Byte b=null;
			while(flag){
				String key=stringbuilder.substring(i,i+count);
				b=map.get(key);
				if(b==null){
					count++;
				}else{
					flag=false;
				}
			}
			list.add(b);
			i+=count;
		}
		//将list放入byte[]中并返回
		byte[] b=new byte[list.size()];
		for(int i=0;i<b.length;i++){
			b[i]=list.get(i);
		}
		return b;
	}
	
	
	/**
	 * 将一个byte转换成一个二进制得字符串
	 * @param flag 正数需要补高位为true,flase不需要补高位
	 * @param b 压缩后的byte
	 * @return
	 */
	private static String byteToBitString(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;
		}
	}
	
	/**
	 * 压缩方法
	 * @param bytes 原始字符串得字节数组
	 * @return 根据生成得赫夫曼编码,压缩得到压缩后得赫夫曼编码字节数组
	 */
	private static byte[] huffmanZip(byte[] bytes){
		List<Node> nodes=getNodes(bytes);
		//创建赫夫曼树
		Node huffmanTreeRoot=createHuffmanTree(nodes);
		//生成对应的赫夫曼编码
		Map<Byte,String> huffmanCodes=getCodes(huffmanTreeRoot);
		//根据生成得赫夫曼编码,压缩得到压缩后得赫夫曼编码字节数组
		byte[] huffmanCodeBytes=zip(bytes,huffmanCodes);
		return huffmanCodeBytes;
	}
	/**
	 * 
	 * @param bytes 原始的字符串对应的字节数组
	 * @param huffmanCodes 生成赫夫曼编码map
	 * @return 返回处理后的byte[]
	 * 举例:原始字符串得到的赫夫曼编码是一长串01字符串,8位对应一个byte ,放入到huffmanCodeBytes
	 * 如:huffmanCodeBytes[0]=10101000(补码),减1取反得到原码11011000=-88
	 * 
	 */
	private static byte[] zip(byte[] bytes,Map<Byte,String> huffmanCodes){
		StringBuilder stringBuilder=new StringBuilder();
		//遍历bytes数组,得到编码
		for(byte b : bytes){
			stringBuilder.append(huffmanCodes.get(b));
		}
		
		//得到huffmanCodeBytes长度,8位一截
		int len=(stringBuilder.length()+7)/8;
		
		byte[] huffmanCodeBytes=new byte[len];
		
		int index=0;
		for(int i=0;i<stringBuilder.length();i=i+8){
			String strByte;
			if(i+8>stringBuilder.length()){
				strByte=stringBuilder.substring(i);
			}else{
				strByte=stringBuilder.substring(i,i+8);
			}
			//将strByte转换成byte放入到huffmanCodeBytes中
			huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte, 2);
			index++;
		}
		return huffmanCodeBytes;
		
	}
	
	
	
	
	/**
	 * 功能:将传入的node节点的所以叶子节点得到其赫夫曼编码,并放入huffmancodes集合中
	 * @param node 传入节点
	 * @param code 路径:左子节点为0,右子节点为1
	 * @param stringbuilder 用于拼接路径
	 */
	private static void getCodes(Node node,String code,StringBuilder stringbuilder){
		StringBuilder stringbuilder2=new StringBuilder(stringbuilder);
		//将code加到stringbuilder2中
		stringbuilder2.append(code);
		if(node!=null){
			if(node.data==null){
				//向左递归
				getCodes(node.left,"0",stringbuilder2);
				//向右递归
				getCodes(node.right,"1",stringbuilder2);
			}else{
				//找到叶子节点
				huffmanCodes.put(node.data, stringbuilder2.toString());
			}
		}
		
	}
	
	//为调用方便,重载getCodes方法
	private static Map<Byte,String> getCodes(Node root){
		if(root==null){
			return null;
		}
		//处理左子树
		getCodes(root.left,"0",stringbuilder);
		//处理右子树
		getCodes(root.right,"1",stringbuilder);
		return huffmanCodes;
		
	}
	
	
	
	/**
	 * 
	 * @param bytes 接受字节数组
	 * @return 返回List节点 如[Node[data=97,value=5]]
	 */
	private static List<Node> getNodes(byte[] bytes){
		ArrayList<Node> nodes = new ArrayList<Node>();
		//遍历bytes,统计每一个字节出现的次数
		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);
			}
		}
		
		//遍历Map,将每一个键值对转换成一个Node对象,保存在List中
		for(Map.Entry<Byte, Integer> entry:counts.entrySet()){
			nodes.add(new Node(entry.getKey(),entry.getValue()));
		}
		return nodes;
	} 
	//前序遍历
//	public static void preOrder(Node root){
//		if(root!=null){
//			System.out.println(root);
//			if(root.left!=null){
//				preOrder(root.left);
//			}
//			if(root.right!=null){
//				preOrder(root.right);
//			}
//		}else{
//			System.out.println("空树");
//		}
//	}
	
	
	
	//生成哈夫曼树
	public static Node createHuffmanTree(List<Node> nodes){
		
		while(nodes.size()>1){
			//从小到大排序
			Collections.sort(nodes);
			
			Node left=nodes.get(0);
			Node right=nodes.get(1);
			
			Node parent=new Node(null,left.value+right.value);
			parent.left=left;
			parent.right=right;
			
			nodes.remove(left);
			nodes.remove(right);
			nodes.add(parent);
		}
		return nodes.get(0);
	}

}
//节点类
class Node implements Comparable<Node>{
	Byte data;
	int value;
	Node left;
	Node right;
	
	public Node(Byte data,int value){
		this.value=value;
		this.data=data;
	}

	@Override
	public String toString() {
		return "Node [data="+data+"  value=" + value + "]";
	}

	@Override
	public int compareTo(Node o) {
		// TODO Auto-generated method stub
		//从小到大排序
		return this.value-o.value;
	}
	

	
}

你可能感兴趣的:(java,算法,数据结构)