基于哈夫曼编码实现的压缩文件的程序(后期可以做个web项目)

代码字里行间都有解释
可以看看
这中间借鉴了别人的一些算法

1.压缩

package com.example.Zip_.HuffmanCode.zip;

import java.io.*;
import java.util.*;

/*
    这里我会将压缩的文件写入 xxx.zip文件中(创建File对象用于创建次文件)
 */
public class Zip {
    public static void main(String[] args) throws IOException{
        long start = System.currentTimeMillis();
        new Zip(new Scanner(System.in).next());
        long end = System.currentTimeMillis();
        System.out.println("expend time -----> "+(end - start)+" ms");
    }
    public Zip(String path) throws IOException {
        getResources resources = new getResources(path);

        int[] ints = resources.getInts();

        BufferedInputStream bufferedInputStream = resources.getFile();

        BufferedOutputStream bufferedOutputStream = getOutputStream(path);
        //首先写入文件名字
        writeFileName(bufferedOutputStream);

        writeCodeLength(resources.length,bufferedOutputStream);



        List<Node> nodes = resources.getNodes(ints);

        Huffman huffman = new Huffman(nodes);

        Node root = huffman.root;

        HashMap<Integer,String> map = new HashMap<>();
        huffman.pre(root,map,"");
        writeHasMap(map,bufferedOutputStream);
        writeHuffmanCodeString(bufferedInputStream,bufferedOutputStream,map);

        bufferedInputStream.close();
        bufferedOutputStream.close();
        map.clear();
        nodes.clear();
        root.clear();
    }
    private String FileName = "";
    private BufferedOutputStream getOutputStream(String path) throws FileNotFoundException {
        int index = path.lastIndexOf(".");
        this.FileName = path ;
        path = path.substring(0,index);
        path += ".zip";
        new File(path+".zip");
        return new BufferedOutputStream(new FileOutputStream(path));
    }
    private void writeFileName(BufferedOutputStream outputStream) throws IOException {
        outputStream.write(FileName.length());
        for (int i = 0 ; i  < FileName.length() ; i ++){
            outputStream.write(FileName.charAt(i));
        }
    }
    private void writeCodeLength(int CodeLength , BufferedOutputStream outputStream) throws IOException {
        String Code = "";
        /*
            注意我这里存进去是32位的二进制
            在我解压的时候我就可以按照这样的规则拿到 length(截取前32位的01字符)
         */
        for (int i = 0x80000000 ; i != 0 ; i >>>= 1){
            Code += (CodeLength & i) == 0 ? '0' : '1';
        }

        for (int j = 0 ; j < Code.length() ; j ++){
            writeBit(Code.charAt(j)-'0',outputStream);
        }
    }
    private void writeHasMap(HashMap<Integer,String> map , BufferedOutputStream outputStream) throws IOException {
        //首先写入哈希表中每个value的长度
        for (int i = 0 ; i < 256 ; i ++){
            if (map.containsKey(i)){
                String s = map.get(i);
                outputStream.write((byte) s.length());
            }else {
                outputStream.write((byte)0);
            }
        }
        //然后写入哈希表的value
        for (int j = 0 ; j < 256 ; j++){
            if (map.containsKey(j)){
                String code = map.get(j);
                for (int i = 0 ; i < code.length() ; i ++){
                    char ch = code.charAt(i);
                    writeBit(ch-'0',outputStream);
                }
            }
        }
    }
    public void writeHuffmanCodeString(BufferedInputStream inputStream , BufferedOutputStream outputStream , HashMap<Integer,String> map) throws IOException {
        int value = inputStream.read();
        while (value != -1){
            String code = map.get(value);
            for (int i = 0; i < code.length(); i++) {
                char ch = code.charAt(i);
                writeBit(ch-'0',outputStream);
            }
            value = inputStream.read();
        }
        //注意如果剩下的不满足八个01串,我们要全部写入
        if (count != -1)outputStream.write(buffer);
    }

    private int count = 7 ;
    private int buffer = 0 ;
    public void writeBit(int date , BufferedOutputStream bufferedOutputStream) throws IOException {
        int k = date << count ;
        buffer = buffer | k ;
        count--;
        if (count == -1){
            bufferedOutputStream.write(buffer);
            count = 7 ;
            buffer = 0 ;
        }
    }
}
class getResources{
    String path = "";
    int length ;
    public getResources(String path){
        this.path = path ;
    }

    public BufferedInputStream getFile(){
        FileInputStream fileInputStream;
        BufferedInputStream bufferedInputStream = null;
        try {
            fileInputStream = new FileInputStream(path);
            bufferedInputStream = new BufferedInputStream(fileInputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return bufferedInputStream;
    }
    public int[] getInts() throws IOException {
        int[] ints = new int[256];
        BufferedInputStream bufferedInputStream = getFile();
        int value = bufferedInputStream.read();
        while (value != -1){
            this.length++;
            ints[value] ++;
            value = bufferedInputStream.read();
        }
        bufferedInputStream.close();
        return ints;
    }

    public List<Node> getNodes(int[] ints){

        List<Node> nodes = new ArrayList<>();
        for (int i = 0; i < ints.length; i++) {
            if (ints[i] != 0){
                nodes.add(new Node(ints[i],i));
            }
        }
        return nodes;
    }
}

class Node implements Comparable<Node>{
    Node left ;

    Node right;
    //权重
    int weight ;
    //字节
    int date;
    public Node(int weight){
        this.weight = weight;
    }
    public Node(int weight , int date){
        this.weight = weight;
        this.date =date ;
    }
    @Override
    public int compareTo(Node o) {
        return this.weight - o.weight;
    }
    public void clear(){
        left = null;
        right = null;
        weight = 0 ;
        date = 0 ;
    }
}

class Huffman{
    Node root = null ;

    /**
     * 构建哈夫曼树
     * @param nodes 叶子结点集合
     */
    public Huffman(List<Node> nodes){
        Collections.sort(nodes);
        while (nodes.size() != 1){
            Collections.sort(nodes);
            Node left = nodes.remove(0);
            Node right = nodes.remove(0);
            root = new Node(left.weight+right.weight);
            root.left = left;
            root.right = right;
            nodes.add(root);
        }
        this.root = nodes.remove(0);
    }

    /**
     * 构建哈夫曼表(前序遍历获得)
     * @param root 哈夫曼树
     * @param map 待添加键值对哈夫曼编码表
     * @param str 用于拼接字符串
     */
    public void pre(Node root, Map<Integer,String> map, String str){
        if(root == null)return;
        if(root.left == null && root.right == null){
            map.put(root.date,str);
            return;
        }
        if(root.left != null){
            pre(root.left,map,str+"0");
        }if(root.right != null){
            pre(root.right,map,str+"1");
        }
    }
}

解压

package com.example.Zip_.HuffmanCode.unzep;

import java.io.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Scanner;

public class deCode {
    public static void main(String[] args) throws IOException{
        long start = System.currentTimeMillis();
        System.out.print("请输入您待解压文件的路径(绝对路径):");
        new deCode(new Scanner(System.in).next());
        long end = System.currentTimeMillis();
        System.out.println("expend time "+(end - start) +" ms");
    }
    public deCode(String path) throws IOException{
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(path));

        //获得文件名的长度
        int FileNameLength = getFileNameLength(bufferedInputStream);

        //获得文件名
        String FileName = getFileName(bufferedInputStream,FileNameLength);

        //创建一个解压后的文件(根据原文件的.xx后缀觉得创建的文件为什么类型)
        String Path = FileName.substring(0,FileName.lastIndexOf("/"));
        String type = FileName.substring(FileName.lastIndexOf("."));
        System.out.print("请输入您解压后文件的name : ");
        FileName = Path+"/"+new Scanner(System.in).next()+type;
        File newFile = new File(FileName);

        //创建一个文件输入流
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(newFile));
        //获得哈夫曼编码字符串的总长度
        int HuffmanCodeLength = getHuffmanCodeLength(bufferedInputStream);
        //获得value的长度数组
        int[] value_length = getvalueLength(bufferedInputStream);

        //还原反转的哈夫曼编码表
        HashMap<String,Integer> map = getHuffmanCodeMap(value_length,bufferedInputStream);

        //利用反转的map将原文件还原出来
        readFile(bufferedOutputStream,bufferedInputStream,map,HuffmanCodeLength);

        bufferedOutputStream.close();
        bufferedInputStream.close();
        map.clear();
    }

    /**
     *
     * @param inputStream 流
     * @return 返回原文件的字节总个数
     * @throws IOException io异常
     */
    private int getHuffmanCodeLength(BufferedInputStream inputStream) throws IOException {
        int value;
        int CodeLength = 0 ;
        int k = 24;
        //得到一个,将十进制的四个数转换成8个单位的二进制数拼接后,再转换成2进制的结果。(这里的算法借鉴了别人的)
        for (int i = 0 ; i < 4 ; i++){
            value = inputStream.read();
            int tt = value << k;
            k = k - 8;
            CodeLength |= tt;
        }
        return CodeLength;
    }

    /**
     *
     * @param inputStream  流
     * @return 返回一个记录哈希表value(哈夫曼编码)的长度的数组(便于后续的反转哈希表的构建)
     * @throws IOException 异常
     */
    private int[] getvalueLength(BufferedInputStream inputStream) throws IOException {
        int[] value = new int[256];
        for (int i = 0; i < 256; i++) {
            int length = inputStream.read();
            value[i] = length;
        }
        return value;
    }

    /**
     *
     * @param inputStream 流
     * @return 首先第一件事,读取文件名的长度并返回
     * @throws IOException 异常
     */
    private int getFileNameLength(BufferedInputStream inputStream) throws IOException {
        return inputStream.read();
    }

    /**
     *
     * @param inputStream 流
     * @param FileNameLength 文件名的长度
     * @return  返回原文件的名字
     * @throws IOException 异常
     */
    private String getFileName(BufferedInputStream inputStream , int FileNameLength) throws IOException {
        StringBuilder FileName = new StringBuilder();
        while (FileName.length() < FileNameLength){
            int value = inputStream.read();
            FileName.append((char) value);
        }
        return FileName.toString();
    }


    /*
            这里的list模仿一个队列,
            便于后续的每8位一读取的操作
     */
    LinkedList<Integer> list = new LinkedList<>();

    /**
     *
     * @param value_length 这就是上面我们获得的记录哈希表value(哈夫曼编码)的长度的数组
     * @param inputStream 流
     * @return 返回一个重建好的反转哈希表(便于后续还原原文件的比对操作)
     * @throws IOException 流
     */
    public HashMap<String,Integer> getHuffmanCodeMap(int[] value_length , BufferedInputStream inputStream) throws IOException{
        HashMap<String,Integer> map = new HashMap<>();
        int value;
        /*
            这里就巧妙的运用队列的特性来实现
         */
        for (int i = 0 ; i < 256 ; i ++){
            String s = "";
            if (value_length[i] != 0){
                int count = 0;
                while (count < value_length[i]){
                    if (list.size() == 0){
                        value = inputStream.read();
                        read(value);
                    }
                    s += list.get(0);
                    list.remove(0);
                    count++;
                }
                map.put(s,i);
            }
        }
        return map;
    }

    /**
     * 还原原文件-------也就是最重要的操作之一
     * @param outputStream 流
     * @param inputStream 流
     * @param map 上面构造好的反转哈希表
     * @param HuffmanCodeLength 原文件字节的总个数
     * @throws IOException 异常
     */
    private void readFile(BufferedOutputStream outputStream,BufferedInputStream inputStream,HashMap<String,Integer> map , int HuffmanCodeLength) throws IOException {
        int count = 0 ;
        String s = "";
        while (count < HuffmanCodeLength){
            if (list.size() == 0){
                int value = inputStream.read();
                if (value == -1)break;
                read(value);

            }
            s += list.get(0);
            list.remove(0);
            if (map.containsKey(s)){
                outputStream.write(map.get(s));
                s = "";
                count++;
            }
        }
    }

    /**
     * 我觉得这下面的代码,是整个代码的核心代码。
     * 作用是:将每个字节转换成8各单位的二进制01串(当然我们这里的0101串是存放在队列中的,这就是精髓的地方)
     */
    private int count = 7;
    private void read(int date){
        for (int i = 0 ; i < 8 ; i ++){
            int t = date >> count;
            count -- ;
            if (count == -1) count = 7;
            t = t & 1;
            list.add(t);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值