java实现哈夫曼编码文件解压
java实现哈夫曼编码文件解压
解压新增代码
/**
* 解压文件
*
* @param src 压缩文件的全路径
* @param dstFile 压缩后存放的路径
*/
private static void unZipFile(String src, String dstFile) {
FileInputStream is = null;
ObjectInputStream ois = null;
FileOutputStream os = null;
try {
is = new FileInputStream(src);
// 创建对象输入流
ois = new ObjectInputStream(is);
// 读取
byte[] huffmanBtyes = (byte[]) ois.readObject();
Map<Byte,String> zipMap = (Map<Byte, String>) ois.readObject();
// 解码
byte[] bytes = unZip(zipMap, huffmanBtyes);
// 写入目标文件
os = new FileOutputStream(dstFile);
os.write(bytes);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
is.close();
ois.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
测试方法
public static void main(String[] args) {
String srcFile = "D:\\图片\\dst.zip";
String destFile = "D:\\图片\\new.jpg";
unZipFile(srcFile,destFile);
System.out.println("解压成功");
}
全代码
public class HuffmanEncodingDemo {
public static void main(String[] args) {
String srcFile = "D:\\图片\\dst.zip";
String destFile = "D:\\图片\\new.jpg";
unZipFile(srcFile,destFile);
System.out.println("解压成功");
}
/**
* 解压文件
*
* @param src 压缩文件的全路径
* @param dstFile 压缩后存放的路径
*/
private static void unZipFile(String src, String dstFile) {
FileInputStream is = null;
ObjectInputStream ois = null;
FileOutputStream os = null;
try {
is = new FileInputStream(src);
// 创建对象输入流
ois = new ObjectInputStream(is);
// 读取
byte[] huffmanBtyes = (byte[]) ois.readObject();
Map<Byte,String> zipMap = (Map<Byte, String>) ois.readObject();
// 解码
byte[] bytes = unZip(zipMap, huffmanBtyes);
// 写入目标文件
os = new FileOutputStream(dstFile);
os.write(bytes);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
is.close();
ois.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 压缩文件
*
* @param src 压缩文件的全路径
* @param dstFile 压缩后存放的路径
*/
private static void zipFile(String src, String dstFile) {
FileInputStream is = null;
FileOutputStream os = null;
ObjectOutputStream oos = null;
try {
// 创建IO流
is = new FileInputStream(src);
// 创建和源文件相同大小的byte数组
byte[] bytes = new byte[is.available()];
// 读取文件
is.read(bytes);
// 解压
byte[] huffmanZip = huffmanZip(bytes);
// 创建文件输出流存放压缩文件
os = new FileOutputStream(dstFile);
// 创建和输出流相关的对象输出流
oos = new ObjectOutputStream(os);
// 这里以对象的方式进行输出压缩文件,方便恢复
oos.writeObject(huffmanZip);
// 最后将编码表写入
oos.writeObject(huffmanCodes);
} catch (Exception e) {
System.out.println(e);
} finally {
try {
is.close();
os.close();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 解码
*
* @param huffmanCodes 编码表
* @param bytes 需要解码的字节数组
*/
private static byte[] unZip(Map<Byte, String> huffmanCodes, byte[] bytes) {
// 拿到对应的字符串
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
// 判断是不是最后一个字节
boolean flag = (i == bytes.length - 1);
// 不是则为真
String s = byteToBitString(!flag, bytes[i]);
stringBuilder.append(s);
}
// 拿到之后按照编码表解码
// 先将编码表调换
HashMap<String, Byte> unzipMap = new HashMap<>();
Set<Map.Entry<Byte, String>> entries = huffmanCodes.entrySet();
for (Map.Entry<Byte, String> entry : entries) {
unzipMap.put(entry.getValue(), entry.getKey());
}
// 创建集合存放byte
ArrayList<Byte> byteArrayList = new ArrayList<>();
// 解码并存放
for (int i = 0; i < stringBuilder.length(); ) {
// 小计数器
int count = 1;
boolean flag = true;
Byte b = null;
while (flag) {
// 从i开始取出n个字节进行判断
String value = stringBuilder.substring(i, i + count);
b = unzipMap.get(value);
if (b == null) {
++count;
} else {
flag = false;
}
}
byteArrayList.add(b);
i += count;
}
// 把list转成数组返回
byte[] result = new byte[byteArrayList.size()];
for (int i = 0; i < byteArrayList.size(); i++) {
result[i] = byteArrayList.get(i);
}
return result;
}
/**
* 将一个byte转成一个二进制的字符串
*
* @param flag 标志是否需要补高位,true表示需要,false表示不需要
* @return 按补码返回
*/
public static String byteToBitString(boolean flag, byte b) {
int temp = b;
// 如果是最后一个字节不需要补高位
if (flag) {
// 按位与
temp |= 256;
}
String str = Integer.toBinaryString(temp);
if (flag) {
str = str.substring(str.length() - 8);
}
return str;
}
/**
* 封装,方便调用(字节数组)
*
* @param bytes 传入的字节数组
* @return 经过哈夫曼编码后的字节数组
*/
private static byte[] huffmanZip(byte[] bytes) {
// 转成Node集合
List<Node> nodes = getNodes(bytes);
// 创建哈夫曼树
Node huffmanTree = createHuffmanTree(nodes);
// 创建编码表
HashMap<Byte, String> encoding = createEncoding(huffmanTree);
// 压缩并返回
return zip(bytes, huffmanCodes);
}
/**
* 封装,方便调用(字符串)
*
* @param str 传入的字符串
* @return 经过哈夫曼编码后的字节数组
*/
private static byte[] huffmanZip(String str) {
byte[] bytes = str.getBytes();
// 转成Node集合
List<Node> nodes = getNodes(bytes);
// 创建哈夫曼树
Node huffmanTree = createHuffmanTree(nodes);
// 创建编码表
HashMap<Byte, String> encoding = createEncoding(huffmanTree);
// 压缩并返回
return zip(bytes, huffmanCodes);
}
/**
* 将字节数组根据编码表压缩
*
* @param bytes 被转换的字节数组
* @return 压缩后的数组
*/
private static byte[] zip(byte[] bytes, HashMap<Byte, String> huffmanCodes) {
StringBuilder builder = new StringBuilder();
// 遍历bytes
for (byte b : bytes) {
// 根据编码表拿到对应的编码
String code = huffmanCodes.get(b);
builder.append(code);
}
// 编码后再次压缩,转成byte数组
// 先得到byte[]的长度
int length = builder.length();
int len;
// 或者写成 len = (length + 7) / 8;
if (length % 8 == 0) {
len = length / 8;
} else {
len = length / 8 + 1;
}
byte[] result = new byte[len];
// 每8位对应
int index = 0;
for (int i = 0; i < length; i += 8) {
String str;
if (i + 8 < length) {
// 够八位
str = builder.substring(i, i + 8);
} else {
// 最后不够八位
str = builder.substring(i);
}
// 转成一个byte存入
result[index++] = (byte) Integer.parseInt(str, 2);
}
return result;
}
static HashMap<Byte, String> huffmanCodes = new HashMap<>();
static StringBuilder stringbuilder = new StringBuilder();
/**
* 为了方便调用和使用,重载
*/
private static HashMap<Byte, String> createEncoding(Node root) {
if (root == null) {
return null;
}
// 为了减少root的一次
createEncoding(root.left, "0", stringbuilder);
createEncoding(root.right, "1", stringbuilder);
return huffmanCodes;
}
/**
* 根据哈夫曼树创建对应的哈夫曼编码表
*
* @param node 传入的节点
* @param code 路径:左字节为0,右子节点为1
* @param stringBuilder 用于拼接路径
*/
private static void createEncoding(Node node, String code, StringBuilder stringBuilder) {
StringBuilder builder = new StringBuilder(stringBuilder);
builder.append(code);
if (node != null) {
// 判断是不是应该编码的
if (node.data == null) {
// 左右递归
createEncoding(node.left, "0", builder);
createEncoding(node.right, "1", builder);
} else {
// 如果是
huffmanCodes.put(node.data, builder.toString());
}
}
}
/**
* 根据Node集合创建哈夫曼树
*/
private 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(left.weight + right.weight);
parent.left = left;
parent.right = right;
nodes.remove(left);
nodes.remove(right);
nodes.add(parent);
}
return nodes.get(0);
}
/**
* 将字符数组按出现个数转换成一个个Node,并存到List返回
*
* @param bytes 被转换的字符数组
* @return 转换后的list
*/
private static List<Node> getNodes(byte[] bytes) {
ArrayList<Node> nodes = new ArrayList<>();
// 遍历统计出现的次数
HashMap<Byte, Integer> map = new HashMap<>();
for (byte b : bytes) {
Integer integer = map.get(b);
if (integer == null) {
map.put(b, 1);
} else {
map.put(b, integer + 1);
}
}
// 将map里的元素转成一个个Node
Set<Map.Entry<Byte, Integer>> entries = map.entrySet();
for (Map.Entry<Byte, Integer> entry : entries) {
Node node = new Node(entry.getKey(), entry.getValue());
// 并加入list
nodes.add(node);
}
return nodes;
}
}
/**
* 节点类
*/
class Node implements Comparable<Node> {
Byte data;
int weight;
Node left;
Node right;
public Node(int weight) {
this.weight = weight;
}
public Node(Byte data, int weight) {
this.data = data;
this.weight = weight;
}
/**
* 前序遍历
*/
public void preOrder() {
System.out.println(this);
if (this.left != null) {
this.left.preOrder();
}
if (this.right != null) {
this.right.preOrder();
}
}
@Override
public int compareTo(Node o) {
return this.weight - o.weight;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
", weight=" + weight +
'}';
}
}