基于哈夫曼压缩算法的压缩与解压实现(Java)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/jiangying_emma/article/details/82426810

如果需要对一个文件进行压缩,第一步是提取出文件中的内容信息;第二步是对其中的字符等进行统计,获得压缩编码;第三步是把压缩编码及解压文件需要的信息存入一个文件(或者生成新文件并存入)。这样对文件的压缩就完成了。

下面就是文件的解压,第一步还是读入文件,即从前面生成的含有压缩编码及解压信息的文件中读取信息内容;第二步是整理出对应的字符编码信息;第三步是按照第二步还原出来的解码信息将文件还原。这样就完成了一个文件的编码压缩和解压。

接下来介绍哈夫曼压缩算法,这个算法适合压缩具有较高信息重复率的文件,比如一个文件的内容是:“aaaaaaaaaaaaaaabbbbbbbbbbbbbbbabababab….”。对于这串字符,所包含的不同字符个数较少,且同一个字符的重复率较高,经过哈夫曼编码压缩后可以大大的节省空间。而对于其他的文件,比如读取出来是“qwertyuiopasdfghjklzxcvbnm…..”。这一串字符所包含的不同的字符个数较多而且重复率较低,如果运用哈夫曼压缩算法进行压缩,就会得到一个比原文件大几倍的“压缩文件”,所以还不如不压缩。

具体压缩算法如下:首先从文件中读取信息,我们假定是一串字符;接下来统计这一串字符中不重复的字符有哪些及其出现的次数(权重);然后根据字符的权重建立哈夫曼二叉树;再接着对每一个字符编码,得到唯一的‘0100…..’这样的01编码表示;然后用每一个字符的01编码替换原字符串中出现的该字符(这样看起来似乎01编码信息比原本的字符串长度还要长);所以接下来还需要对这一串01编码进行处理,最终将处理完的编码信息和解码所需的字符与其唯一的01编码信息存入同一个文件,当然怎么存是根据写程序的人打算如何解压来决定的,如果不知道存入信息的顺序,即使拿到压缩文件也很难解压。

前面还有两个问题没有解决:1、如何根据哈夫曼二叉树对字符进行编码,2、如何对比原字符串还要长的01编码进行处理。请看下图:
这里写图片描述
假设一串字符里面不重复的字符仅有:a,b,c,d,e,且各个字符出现的次数(权重)为:1,3,5,6,12,那么按照权重建立哈夫曼二叉树,就得到每一个字符的唯一位置,下面对字符进行编码:从根节点开始,编码String code=”“;往左就code+=’0’,往右就code+=’1’;这样直到找到对应的字符,停止编码并得到唯一的01编码code,比如上图的‘a’经过上述步骤编码就得到‘1100‘。

接着使用上面的例子,加入一串字符为“aabbcde”,替换为对应的01编码就得到“1100110011011101111100”,很明显这串01编码比原来的“aabb。。”要长,所以接下来就对这一串01编码进行处理:将其划分为8个一组,上面的经过划分后就得到:“11001100”,“11011101”,“111100”,我们可以看到大多数情况下经过划分后都会在最后产生不足8位的情况,这时候可以采用在最后补0的方法进行补全,上面的经过补0后得到“11001100”,“11011101”“11110000”,然后将8位的01转为对应的数字并存入数组中,经过这一步后我们得到三个数字,在存入文件时在吧数字转为byte型,这样一来在文件中就只需要3个字节就可以把上面的“aabb….”给存完,明显节约了空间,也就实现了压缩。

好了就文件的压缩而言,到这里好像没什么问题,但我们压缩文件只是为了节省存储空间,最终还需要解压出来,如果没有存入解压信息就无法完成解压,那么经过压缩获得的文件就毫无意义。所以我们还需要存入一下额外的信息:字符对应的编码:(a:1100)(b:1101)(c:111)(d:10)(e:0)、没有补0时01编码的长度或补0的个数,因为我们需要把补的0去掉、以及由01码得到的数组数据。这样一个可以解压的压缩文件就完成了。

下面再接着聊如何解压,首先需要从待解压文件中读取信息;接着还原出编码对应的字符:(a:1100)(b:1101)(c:111)(d:10)(e:0)、没有补0时01编码的长度或补0的个数、以及由01码得到的数组数据。;然后根据把数组数据还原为01编码并去掉末尾补加的0,;再然后从01码的开始一个个与字符编码进行比对,还原出原本的字符串;最后将还原出来的字符串存入文件就完成了文件的解压。

下面是我用Java实现的文件压缩和解压:
这里写图片描述
上面是原文件的内容,下面是经过压缩,从压缩文件中解压出来的文件内容,经过比对内容一致。
这里写图片描述
这里上面一个文件的内容是完整的压缩文件(包含解压信息),下面一个是仅有01对应的数组数据信息。这里原文件大小是2.19KB,解压还原出来的文件也是2.19KB,包含完整压缩信息的文件为1.63KB,仅包含压缩后的编码信息的文件为1.18KB.这里也印证了前面所说的哈夫曼算法适合压缩具有较高重复率的文件,对重复率不高的文件压缩比不高,效果较差。

接下来给大家分享一下我写的实现哈夫曼压缩的Java程序,大家感兴趣的可以在这里下载源码:
https://pan.baidu.com/s/1T3258XDwf72IJJ499ERYMw#list/path=%2F
虽然我写了较多的注释,但调用类里面的函数还是比较麻烦,并且有时不记得该调用哪个函数了,所以就对每个类加了一个对外的接口函数,只要知道哪个类实现什么功能就行,然后传入对应的数据,并获得处理后的信息。

最后我的程序在使用时的调用界面如下:

import java.io.File;

public class Main {

    //最终总函数,其他函数也可以调用压缩及解压的相关类完成功能
    public void toMain(){
        //定义输入(待压缩)文件
        File from=new File("C:\\Users\\user\\Desktop\\a.txt");
        comFILEIN comfilein=new comFILEIN();
        //从待压缩文件中读取有效字符串
        String str=comfilein.getfilestr(from);
        //根据读取的有效字符串,完成压缩,获得有关信息并保存在conpress实例化的对象中
        compress newcom=new compress();
        newcom.tocompress(str);
        //从保存压缩信息的对象中输出到新建文件中
        comFILEOUT comfileout=new comFILEOUT();
        comfileout.outtofile(newcom);
        //待解压文件
        File in=new File("C:\\Users\\user\\Desktop\\outa.txt");
        //从待解压文件中读取压缩信息,保存在解压uncompress类的实例化对象中
        uncomFILEIN unfilein=new uncomFILEIN();
        unfilein.unfiletouncompress(in);
        uncompress uncom=new uncompress();
        uncom.touncompres(unfilein.mapchtobi, unfilein.mapbitoch, unfilein.d, unfilein.savenum);
        //将解压还原的信息放在新建文件中
        uncomFILEOUT uncomout=new uncomFILEOUT();
        uncomout.uncomtofile(uncom);
    }
    public static void main(String[] args){
        Main M=new Main();
        M.toMain();
    }
}

压缩和解压是两个不同的功能,所以写不同的类进行实现,对文件进行压缩时需要读入文件,对文件信息进行处理,写出压缩文件,所以在压缩的部分又分别写了文件读入和写出功能的类,对解压时也同样分别写了文件读入和写出的类。所有类的调用都通过对外接口进行信息传入和传出。

展开阅读全文

没有更多推荐了,返回首页