哈夫曼编码压缩文件&解压文件

代码
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;
    }
}

结果
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值