java学习笔记(数据结构与算法9)课程设计-压缩文件和解压文件,思路代码注释。

 

目录

 压缩思路:

 解压思路:

 注意:

代码: 

结果:


 压缩思路:

1.先将要压缩的句子或者文件内容存入一个byte数组中
2.处理字符串,存入哈希表<byte值,出现频率>
3.根据字符串处理后的哈希表创建一个哈夫曼树,字符都是叶子节点,出现频率越高,距离根结点越近
4.生成哈夫曼编码表,保存在哈希表中<Byte值,编码>,比如字母i(本身有一个byte)会对应一个比如001这样的编码。
  byte值对应的编码是根据从根结点到该节点的路径,往左走一步在编码后面加个“0”,右走加“1”
5.根据哈夫曼编码表得到字符串对应的哈夫曼编码
  要注意的是,这里得到的是2进制的编码,这样的压缩率不会太高,因此要将其转化为十进制存入byte数组(每个byte可以存8个二进制编码)

 解压思路:

1.将压缩文件中的两个对象(哈夫曼编码表和文件编码)拿出来
2.处理存储哈夫曼编码的哈希表(反转:<Byte,String>-><String,Byte>)方便后面根据String查找Byte
3.根据反转后的哈希表和压缩文件编码,变量编码找出对应的字符。

 注意:

由于我们要将压缩规则一并传入压缩文件中,所以压缩后的文件不一定比原文件小,但如果我们只传入压缩后的数据,压缩率会很高,但由于我们的压缩规则是根据数据得到的,因此无法解压。

代码: 

package ZipFile;
import java.io.*;
import java.util.*;
import java.util.List;

/*
压缩思路:
1.先将要压缩的句子或者文件内容存入一个byte数组中
2.处理字符串,存入哈希表<byte值,出现频率>
3.根据字符串处理后的哈希表创建一个哈夫曼树,字符都是叶子节点,出现频率越高,距离根结点越近
4.生成哈夫曼编码表,保存在哈希表中<Byte值,编码>,比如字母i(本身有一个byte)会对应一个比如001这样的编码。
  byte值对应的编码是根据从根结点到该节点的路径,往左走一步在编码后面加个“0”,右走加“1”
5.根据哈夫曼编码表得到字符串对应的哈夫曼编码
  要注意的是,这里得到的是2进制的编码,这样的压缩率不会太高,因此要将其转化为十进制存入byte数组(每个byte可以存8个二进制编码)
 */
/*
解压思路:
1.将压缩文件中的两个对象(哈夫曼编码表和文件编码)拿出来
2.处理存储哈夫曼编码的哈希表(反转:<Byte,String>-><String,Byte>)方便后面根据String查找Byte
3.根据反转后的哈希表和压缩文件编码,变量编码找出对应的字符。
 */
public class ZipFileTest {
    public static void main(String[] args) {
        ZipTool zipTool=new ZipTool();
        zipTool.zipSentence("Go and just do it. ");
        zipTool.zipFile("C:\\Users\\lenovo\\Desktop\\123.doc","C:\\Users\\lenovo\\Desktop\\yasuo6.zip");
        zipTool.unZipFile("C:\\Users\\lenovo\\Desktop\\yasuo6.zip","C:\\Users\\lenovo\\Desktop\\1234.doc");
    }

}
//前三个方法是封装的功能方法,后面先是压缩用到的方法,最后是解压用到的方法
class ZipTool{
    //这个方法是压缩一句话(可以用来测试压缩率),具体方法的功能参考压缩文件的方法
    public void zipSentence(String s){
        byte[] bytes= s.getBytes();
        System.out.println(bytes.length);
        Map<Byte,Integer> map=dealString(bytes);
        Node node=getHuffmanTree(map);
        Map<Byte,String> huffmanCode=getHuffmanCodeTable(node);
        byte[] result=getHuffmanCode(bytes,huffmanCode);
        System.out.println(result.length);
        System.out.println(Arrays.toString(result));
    }
    //这个方法是压缩文件
    public void zipFile(String source,String pack){
        FileInputStream fileInputStream=null;
        FileOutputStream fileOutputStream=null;
        ObjectOutputStream oos=null;
        try {
            fileInputStream=new FileInputStream(source);
            fileOutputStream=new FileOutputStream(pack);
            //创建一个大小和文件一样的byte数组,available返回还没读的字节数
            byte[] bytes=new byte[fileInputStream.available()];
            //将文件内容读入bytes数组
            fileInputStream.read(bytes);
            //处理byte数组,存入哈希表<byte值,出现频率>
            Map<Byte,Integer> map=dealString(bytes);
            //根据字符串处理后的哈希表创建一个哈夫曼树,字符都是叶子节点,出现频率越高,距离根结点越近
            Node node=getHuffmanTree(map);
            //生成哈夫曼编码表,保存在哈希表中<Byte值,编码>,比如字母i(本身有一个byte)会对应一个比如001这样的编码。
            Map<Byte,String> huffmanCode=getHuffmanCodeTable(node);
            byte[] result=getHuffmanCode(bytes,huffmanCode);
            //这里要以对象的形式将压缩规则(哈夫曼编码表)和压缩的数据传入压缩文件中,方面后面提取规则和数据
            oos=new ObjectOutputStream(fileOutputStream);
            oos.writeObject(huffmanCode);
            oos.writeObject(result);
            oos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fileInputStream!=null){
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(oos!=null){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //这个方法是解压的方法
    public void unZipFile(String pack,String source){
        FileInputStream fileInputStream=null;
        FileOutputStream fileOutputStream=null;
        ObjectInputStream objectInputStream=null;
        try {
            fileOutputStream=new FileOutputStream(source);
            fileInputStream=new FileInputStream(pack);
            objectInputStream=new ObjectInputStream(fileInputStream);
            //按顺序取出哈夫曼编码和压缩的数据
            Map<Byte,String> huffmanCode=(Map<Byte, String>) objectInputStream.readObject();
            byte[] bytes=(byte[]) objectInputStream.readObject();
            //处理哈夫曼编码反转为<编码,Byte值>来方便后面根据编码对照着找byte值
            Map<String,Byte> mapDeal=unZipDeal(huffmanCode);
            byte[] result=unZip(mapDeal,bytes);
            fileOutputStream.write(result);
            fileOutputStream.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(objectInputStream!=null){
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fileOutputStream!=null){
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //处理字符串,存入哈希表<byte值,出现频率>
    public Map<Byte,Integer> dealString(byte[] bytes){
        Map<Byte,Integer> hashMap=new HashMap<>();
        int i=0;
        while(i<bytes.length){
            hashMap.put(bytes[i],hashMap.getOrDefault(bytes[i],0)+1);
            i++;
        }
        return hashMap;
    }
    //下面为压缩用到的方法-----------------------------------------------------------------
    //根据字符串处理后的哈希表创建一个哈夫曼树
    public Node getHuffmanTree(Map<Byte,Integer> map){
        List<Node> list=new Stack<>();
        for(Map.Entry<Byte,Integer> entry:map.entrySet()){
            Node node=new Node(entry.getKey(),entry.getValue());
            list.add(node);
        }
        while(list.size()>1) {
            Collections.sort(list);
            //System.out.println(list.toString());
            Node node1 = list.get(0);
            Node node2 = list.get(1);
            Node newNode = new Node(null, node1.weight + node2.weight);
            newNode.leftNode=node1;
            newNode.rightNode=node2;
            list.remove(0);
            list.remove(0);
            list.add(newNode);
        }
        return list.get(0);
    }
    //生成哈夫曼编码表,保存在哈希表中<Byte值,编码>
    public Map<Byte,String> getHuffmanCodeTable(Node node){
        Map<Byte,String> huffmanCode=new HashMap<>();
        StringBuilder s=new StringBuilder("");
        getHuffmanCodeTable(node,huffmanCode,s);
/*        for(Map.Entry<Byte,StringBuilder> entry:huffmanCode.entrySet()){
            System.out.println(entry.getKey()+"="+entry.getValue());
        }*/
        return huffmanCode;
    }
    //得到哈夫曼编码表
    public void getHuffmanCodeTable(Node node,Map<Byte,String> huffmanCode,StringBuilder s) {
        //System.out.println(node);
        if(node!=null){
            if(node.c!=null){
                huffmanCode.put(node.c,s.toString());
                return;
            }
            StringBuilder stringBuilderL=new StringBuilder(s);
            getHuffmanCodeTable(node.leftNode,huffmanCode,stringBuilderL.append("0"));
            StringBuilder stringBuilderR=new StringBuilder(s);
            getHuffmanCodeTable(node.rightNode,huffmanCode,stringBuilderR.append("1"));
        }
        return;
    }
    //根据哈夫曼编码表得到字符串对应的哈夫曼编码
    public byte[] getHuffmanCode(byte[] bytes,Map<Byte,String> huffmanCodeTable){
        StringBuilder huffmanCode=new StringBuilder();
        for(byte b:bytes){
            huffmanCode.append(huffmanCodeTable.get(b));
        }
/*        System.out.println(huffmanCode.length());
        System.out.println(huffmanCode.toString());*/
        int len=(huffmanCode.length()+7)/8;
        byte[] huffmanCodeByte=new byte[len];
        //i表示字符串生成的哈夫曼编码的索引,index表示存入的byte[]的索引
        for(int i=0,index=0;i<huffmanCode.length();i+=8){
            String temp;
            if(i+8>huffmanCode.length()){
                temp=huffmanCode.substring(i);
            }else{
                //System.out.println(huffmanCode.substring(i,i+8).toString()+"      "+Integer.parseInt(huffmanCode.substring(i,i+8).toString()));
                temp=huffmanCode.substring(i,i+8);
            }
            //System.out.println("temp:"+temp);
            //这里要将二进制转化为10进制存入byte数组中(来节省空间),所以要通过Integer中的parseInt传参使2进制转化为10进制。
            huffmanCodeByte[index]=(byte) Integer.parseInt(temp,2);
            index++;
        }
        return huffmanCodeByte;
        //return huffmanCode.toString();
    }

    //下面为解压用到的方法-----------------------------------------------------------------
    //处理存储哈夫曼编码的哈希表(反转:<Byte,String>-><String,Byte>)方便后面根据String查找Byte
    /*
    * @param * @Param huffmanCode:  读取的哈夫曼编码表
    * @return * @return: java.util.Map<java.lang.String,java.lang.Byte> 反转的哈夫曼编码
    */
    public Map<String,Byte> unZipDeal(Map<Byte,String> huffmanCode){
        Map<String,Byte> dealHuffmanCode=new HashMap<>();
        for(Map.Entry<Byte,String> code:huffmanCode.entrySet()){
            dealHuffmanCode.put(code.getValue(),code.getKey());
        }
        return dealHuffmanCode;
    }
    /*
    * @param * @Param dealHuffmanCode: <String,Byte>这样的哈希表(译码表)
     * @Param bytes:  从压缩文件中读取的byte数组
    * @return * @return: byte[]  解压后的byte数组
    */
    public byte[] unZip(Map<String,Byte> dealHuffmanCode,byte[] bytes){
        StringBuilder stringBuilder=new StringBuilder();//存储bytes数组转化为的二进制码
        boolean flag=true;
        StringBuilder huffmanString=new StringBuilder();
        for(int i=0;i<bytes.length;i++){
            int temp=bytes[i];
            if(i==bytes.length-1&&temp>=0){
                String s=Integer.toBinaryString(temp);
                huffmanString.append(s);
            }else{
                temp|=256;
                String s=Integer.toBinaryString(temp);
                huffmanString.append(s.substring(s.length()-8));
            }

        }
        List<Byte> list=new ArrayList<>();

        for(int i=0;i<huffmanString.length();){
            int temp=i;
            while(true){
                temp++;
                if(dealHuffmanCode.get(huffmanString.substring(i,temp))!=null){
                    list.add(dealHuffmanCode.get(huffmanString.substring(i,temp)));
                    break;
                }
            }
            i=temp;
        }
        byte[] huffmanByte=new byte[list.size()];
        for(int i=0;i<huffmanByte.length;i++){
            huffmanByte[i]=list.get(i);
        }
        return huffmanByte;
    }
}

class Node implements Comparable<Node>{
    Byte c;
    int weight;
    Node leftNode;
    Node rightNode;

    public Node(Byte c, int weight) {
        this.c = c;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Node{" +
                "c=" + c +
                ", weight=" + weight +
                '}';
    }

    @Override
    public int compareTo(Node o) {
        return this.weight-o.weight;
    }
}

结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值