至于赫夫曼编码是什么我就不多介绍了,直接讲解如何压缩和解压吧
这种方法对大部分文件都适用,并且是无损压缩
我写的压缩文件主要分为一下几步
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;
}
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;
}
}
压缩和解压直接调用上面的方法就行
//编写一个方法对文件进行解压
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();
}
}
}
//将生成的赫夫曼编码{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("解压成功");
}
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;
}
}