代码
public class Main {
public static void main(String[] args) {
String srcFile = "e://des.zip";
String desFile = "e://222.jpg";
//zipFile(srcFile,desFile);
unZipFile(srcFile,desFile);
}
static Map<Byte,String> map = new HashMap<Byte,String>();
public static void unZipFile(String srcFile,String desFile){
//创建文件输入流 对象输入流 文件输出流
FileInputStream fis = null;
ObjectInputStream ois = null;
FileOutputStream fos = null;
try {
//对文件输入流和对象输入流进行初始化
fis = new FileInputStream(srcFile);
ois = new ObjectInputStream(fis);
//创建字节数组,接收读取的 经过哈夫曼编码后的 字节数组
byte[] huffmanBytes = (byte[])ois.readObject();
//创建map集合 接收读取的 哈夫曼编码
Map<Byte,String> huffmanCode = (Map<Byte,String>)ois.readObject();
//创建文件输出流
fos = new FileOutputStream(desFile);
//创建字节数组 接收解压之后的 字节数组
byte[] afterUnZipBytes = decode(huffmanCode,huffmanBytes);
//将字节数组写出
fos.write(afterUnZipBytes);
}catch (Exception e){
e.printStackTrace();
}finally {
try {
fis.close();
ois.close();
fos.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
public static void zipFile(String srcFile,String desFile){
//创建文件输入流 输出流 对象输出流
//用对象流 便于恢复
FileInputStream fis = null;
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
//首先创建文件输入流
fis = new FileInputStream(srcFile);
//创建与文件大小相同的字节数组,用于装源文件
byte[] srcBytes = new byte[fis.available()];
//将文件读入字节数组中
fis.read(srcBytes);
//创建字节数组,用于接收经哈夫曼编码后的字节数组
byte[] afterZipBytes = huffmanZip(srcBytes);
//创建输出流
fos = new FileOutputStream(desFile);
oos = new ObjectOutputStream(fos);
oos.writeObject(afterZipBytes);
oos.writeObject(map);//一定要把哈夫曼编码写出去
}catch (Exception e){
e.printStackTrace();
}finally {
try {
fis.close();
fos.close();
oos.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
*
* @param bytes 原始的字节数组
* @return 经过赫夫曼编码后的字节数组
*/
public static byte[] huffmanZip(byte[] bytes){
//首先得到每个节点,封装成集合
List<Node> list = getList(bytes);
//然后利用节点集合,创建赫夫曼树
Node huffmanTree = createHuffmanTree(list);
//根据赫夫曼树,得到每个字符的编码,保存在map中
getCode(huffmanTree,new StringBuilder(),"");
//根据字符的编码,得到最终的字节数组
byte[] huffmanByte = getHuffmanByte(bytes);
return huffmanByte;
}
//得到每个字符与其权值,封装成集合返回
public static List<Node> getList(byte[] bytes){
List<Node> nodeList = new ArrayList<Node>();
//创建map集合 用作存储data 和 weight
HashMap<Byte, Integer> byteIntegerHashMap = new HashMap<>();
for (byte oneByte: bytes){
if (byteIntegerHashMap.get(oneByte) == null){
//说明是第一次添加
byteIntegerHashMap.put(oneByte,1);
}else {
//不是第一次添加
byteIntegerHashMap.put(oneByte,byteIntegerHashMap.get(oneByte) + 1);
}
}
//遍历map集合 将data和weight加到Node
for (Map.Entry<Byte,Integer> entry: byteIntegerHashMap.entrySet()){
nodeList.add(new Node(entry.getKey(),entry.getValue()));
}
return nodeList;
}
//得到哈夫曼树
public static Node createHuffmanTree(List<Node> nodeList){
while (nodeList.size() > 1){
Collections.sort(nodeList);
Node leftNode = nodeList.get(0);
Node rightNode = nodeList.get(1);
Node parent = new Node(null,leftNode.weight + rightNode.weight);
parent.left = leftNode;
parent.right = rightNode;
//System.out.println("leftNode=" + leftNode + "rightNode="+rightNode);
nodeList.remove(leftNode);
nodeList.remove(rightNode);
nodeList.add(parent);
}
return nodeList.get(0);
}
//得到哈夫曼编码值
public static void getCode(Node node,StringBuilder stringBuilder,String code){
//这个if是针对 根节点有可能是null
if (node != null){
//这里需要创建一个新的StringBuilder对象,我目前还不知道为啥 但是不创建就不对
StringBuilder stringBuilder1 = new StringBuilder(stringBuilder);
stringBuilder1.append(code);
//System.out.println("node="+node+"stringBuilder="+stringBuilder1);
if (node.data == null){
//说明是非叶子节点
//它的left和right一定不是null 这是哈夫曼树的特点
getCode(node.left,stringBuilder1,"0");
getCode(node.right,stringBuilder1,"1");
}else {
//把这个叶子节点加到map集合中
map.put(node.data,stringBuilder1.toString());
}
}
}
//返回得到的字节数组
public static byte[] getHuffmanByte(byte[] bytes){
StringBuilder stringBuilder = new StringBuilder();
//得到全是数的序列
for (byte oneByte: bytes){
stringBuilder.append(map.get(oneByte));
}
//将序列按8个一组(一个字节)进行划分
//首先得到分组的数目
int len;
if (stringBuilder.length() % 8 == 0){
//说明是8的整数倍
len = stringBuilder.length() / 8;
}else {
len = stringBuilder.length() / 8 + 1;
}
//然后根据得到的len创建数组
byte[] finalBytes = new byte[len];
int index = 0;
for (int i = 0;i < stringBuilder.length();i += 8){
//finalBytes[index] = stringBuilder.substring(i,i + 8) 这种是不对的 不能直接加
String oneByte;
if (i + 8 > stringBuilder.length()){
//说明有可能一共就少于8个数 也有可能是到了最后一个不足8个数的组
oneByte = stringBuilder.substring(i);
}else {
oneByte = stringBuilder.substring(i,i + 8);
}
finalBytes[index] = (byte)Integer.parseInt(oneByte,2);//这里还要转成byte类型,后面的参数中还有2
index++;
}
return finalBytes;
}
//将一个byte元素转成二进制
public static String byteToBitString(boolean flag,byte b){
int temp = b;//将字节类型转为int类型
if (flag){//需要补高位,如果是最后一个字节,无需补高位
temp |= 256;//按位或
}
String str = Integer.toBinaryString(temp);
if (flag){
return str.substring(str.length() - 8);
}else {
return str;
}
}
//进行解码
/**
*
* @param huffmanMap 表示哈夫曼编码表
* @param huffmanBytes 表示经过哈夫曼编码之后得到的字节数组 [-88,......]
* @return 返回原字节数组[i, ,l,i,.....]
*/
public static byte[] decode(Map<Byte,String> huffmanMap,byte[] huffmanBytes){
//首先将经过哈夫曼编码之后得到的字节数组,转成很长的一串数字
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0;i < huffmanBytes.length;i++){
if (i == huffmanBytes.length - 1){
//是最后一个 无需补高位
stringBuilder.append(byteToBitString(false,huffmanBytes[i]));
}else {
stringBuilder.append(byteToBitString(true,huffmanBytes[i]));
}
}
//这样就得到了 1010100010111111110010001011111111001000101111111100100101001101110001110000011011101000111100101000101111111100110001001010011011100
//得到key和value互换的 哈夫曼编码表
Map<String,Byte> reverseHuffmanMap = new HashMap<>();
for (Map.Entry<Byte,String> entry: huffmanMap.entrySet()){
reverseHuffmanMap.put(entry.getValue(),entry.getKey());
}
//根据反转之后的哈夫曼编码表,得到字节数组
List<Byte> arrayList = new ArrayList<Byte>();//这个地方要Byte 而不是 byte
int i = 0;
while (i < stringBuilder.length()){
//for (int i = 0;i < stringBuilder.length();){
int count = 1;
boolean flag = true;
while (true){
String binaryNum = stringBuilder.substring(i,i + count);
Byte oneByte = reverseHuffmanMap.get(binaryNum);
if (oneByte == null){
//说明没匹配到,那就将count++
count++;
}else {
//说明匹配到了
// System.out.println("oneByte="+oneByte);
arrayList.add(oneByte);
break;
}
}
i = i + count;
}
//}
System.out.println("集合为:"+ arrayList);
//将集合转为byte数组
byte[] finalBytes = new byte[arrayList.size()];
for (int j = 0;j < finalBytes.length;j++){
finalBytes[j] = arrayList.get(j);
}
return finalBytes;
}
}
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;
}
//前序遍历
public void preList(Node root){
if (root == null){
System.out.println("树为空");
return;
}else {
this.preList();
}
}
public void preList(){
System.out.println(this);
if (this.left != null){
this.left.preList();
}
if (this.right != null){
this.right.preList();
}
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
", weight=" + weight +
'}';
}
@Override
public int compareTo(Node o) {
return this.weight - o.weight;
}
}
结果