Java小程序之哈夫曼树与文件压缩和解压缩(二)文件压缩篇

来源:互联网 发布:wsn网络分析器 编辑:程序博客网 时间:2024/05/16 23:49

 Java小程序之哈夫曼树与文件压缩和解压缩(二)文件压缩篇

一、初识压缩与解压缩原理

压缩可以理解为:对文件的加密过程

解压可以理解为:对文件的解密过程

例如:

 - a

 -  b

 -  c

我是谁   -》  abc


二、压缩原理详解
我们都知道,计算机只能存储二进制数据,即计算机中存储的都是0和1;那么我们看的的a、b、c等英文字母和汉字在计算机中使怎么存储的呢?
这就涉及到了编码的问题了,英文用的编码是ASCII,即利用一个字节(8位,也就是8个0和1 组成的01串)来表示各种英文字母以及符号,例如a字母在ASCII编码中是97(十进制),在计算机中的存储是:01100001;所以一个英文字母或者符号只占一个字节;因为汉字众多,远远不止256个,所以汉字的编码需要用两个字节来编码,总共可编码2*16个汉字;所以一个汉字一半占两个字节;常用的汉字编码有UTF-8和GBK等;

现在比如在一个文件中存储有abaabc;那么计算机中存储的应该是01100001(a) 01100010 (b)01100001(a)01100001(a)01100010 (b)01100011 (c),一共占6个字节;如何利用哈夫曼树将这个文件进行压缩呢?构造哈夫曼树,首先的有权值,那么拿什么东西作为权值呢?

我们用文件中某个字出现的次数作为权值来构造哈夫曼树,上面的字符串中a出现了三次,那么把3作为a的权值,b出现了两次,那么把2作为b的权值,同理把1作为c的权值,那么我们得到的哈夫曼树如下图:




 aabbac ==》01100001  01100010 01100001 01100001 01100010 01100011
那么原来的那一串abaabc用哈夫曼编码后为:aabbac ==》1 1 01 01 00
瞧,原来的6个字节现在变成了2个字节了;我们只要把这个新的01串转成十进制整数,再把这个十进制的整数写入到压缩文件中即完成了压缩;

     
三、压缩具体实现思路:

1、读取文件,统计文件中每个字节出现次数(作为构造哈夫曼树的权值)

2、根据次数构建哈夫曼树

3、根据哈夫曼树构建码表

4、再读取文件,通过码表对文件中读取的字节进行加密处理


难点分析:
定义一个256长度的整型数组
定义一个256长度的字符串数组
为什么呢?其一、英文字母和符号加起来正好256个,当读到是97时,正好可以在该位置加1
后面的代码会体现,这里还需要注意的是索引要对应好,比如a在ASCII码中是97,那么,字符串数组索引为97的位置存储的一定要是a字母对应的哈夫曼编码


 

通过哈夫曼压缩后,我们将得到一串压缩后01串,最后,我们需要把这一01串每8个一组,转换长十进制的整数,最后不够8位的补0,把得到的十进制整数写入文件即完成压缩工作了!


、哈夫曼树文件压缩源代码:

哈夫曼节点类:这里需要索引的目通过索引既可以得到该索引的哈夫曼编码,同时可以得打原来的数据

package com.huaxin.compress;/* * 哈夫曼节点类 */public class HuffmNode {//数据域private int data;//索引private int index;//左子节点private HuffmNode left;//右子节点private HuffmNode right;//哈夫曼节点的构造函数public HuffmNode(int data,int index){this.data=data;this.index=index;}//私有属性的封装public int getData() {return data;}public void setData(int data) {this.data = data;}public int getIndex() {return index;}public void setIndex(int index) {this.index = index;}public HuffmNode getLeft() {return left;}public void setLeft(HuffmNode left) {this.left = left;}public HuffmNode getRight() {return right;}public void setRight(HuffmNode right) {this.right = right;}}


压缩以及压缩实现类
package com.huaxin.compress;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.util.LinkedList;public class Compress {public int [] times = new int[256];public String [] HuffmCodes=new String[256];public LinkedList<HuffmNode> list = new LinkedList<HuffmNode>();//统计次数//初始化public Compress(){for (int i = 0; i < HuffmCodes.length; i++) {HuffmCodes[i]="";}}public void countTimes(String path) throws Exception{//构造文件输入流FileInputStream fis = new FileInputStream(path);//读取文件int value=fis.read();while(value!=-1){times[value]++;value=fis.read();}//关闭流fis.close();}//构造哈夫曼树public HuffmNode createTree(){//将次数作为权值构造森林for (int i = 0; i < times.length; i++) {if(times[i]!=0){HuffmNode node = new HuffmNode(times[i],i);//将构造好的节点加入到容器中的正确位置list.add(getIndex(node), node);}}//将森林(容器中的各个节点)构造成哈夫曼树while(list.size()>1) {//获取容器中第一个元素(权值最小的节点)HuffmNode firstNode =list.removeFirst();//获取中新的第一个元素,原来的第一个元素已经被移除了(权值次小的节点)HuffmNode secondNode =list.removeFirst();//将权值最小的两个节点构造成父节点HuffmNode fatherNode =new HuffmNode(firstNode.getData()+secondNode.getData(),-1);fatherNode.setLeft(firstNode);fatherNode.setRight(secondNode);//父节点加入到容器中的正确位置list.add(getIndex(fatherNode),fatherNode);}//返回整颗树的根节点return list.getFirst();}//利用前序遍历获取编码表public void getHuffmCode(HuffmNode root,String code){//往左走,哈夫曼编码加0if(root.getLeft()!=null){getHuffmCode(root.getLeft(),code+"0");}//往右走,哈夫曼编码加1if(root.getRight()!=null){getHuffmCode(root.getRight(),code+"1");}//如果是叶子节点,返回该叶子节点的哈夫曼编码if(root.getLeft()==null && root.getRight()==null){//System.out.println(root.getIndex()+"的编码为:"+code);HuffmCodes[root.getIndex()]=code;}}//压缩文件public void compress(String path,String destpath) throws Exception{//构建文件输出流FileOutputStream fos = new FileOutputStream(destpath);FileInputStream fis = new FileInputStream(path);/**===============将数据写入到文件中================*///读文件,并将对应的哈夫曼编码串接成字符串int value=fis.read();String str = "";while(value!=-1){str+=HuffmCodes[value];//System.out.println((char)value+":"+str);value=fis.read();}System.out.println(str);fis.close();String s="";while(str.length()>=8){s=str.substring(0, 8);int b=changeStringToInt(s);//System.out.println(c);fos.write(b);fos.flush();str=str.substring(8);}int last1=8-str.length();for (int i = 0; i <last1; i++) {str+="0";}s=str.substring(0, 8);//System.out.println(s);int d=changeStringToInt(s);fos.write(d);//补0的个数暂时不写,后面解压缩在写//fos.write(last1);//fos.flush();fos.close();}//插入元素位置的索引public int getIndex(HuffmNode node) {for (int i = 0; i < list.size(); i++) {if(node.getData()<=list.get(i).getData()){return i;}}       return list.size();}//将字符串转换成整数public int changeStringToInt(String s){int v1=(s.charAt(0)-48)*128;int v2=(s.charAt(1)-48)*64;int v3=(s.charAt(2)-48)*32;int v4=(s.charAt(3)-48)*16;int v5=(s.charAt(4)-48)*8;int v6=(s.charAt(5)-48)*4;int v7=(s.charAt(6)-48)*2;int v8=(s.charAt(7)-48)*1;return v1+v2+v3+v4+v5+v6+v7+v8;}}

测试类:

package com.huaxin.compress;public class Test {public static void main(String[] args) throws Exception {//创建压缩对象Compress compress = new Compress();//统计文件中0-255出现的次数compress.countTimes("C:\\Users\\Administrator\\Desktop\\test.txt");//构造哈夫曼树,并得到根节点HuffmNode root=compress.createTree();//得到哈夫曼编码compress.getHuffmCode(root, "");//压缩文件compress.compress("C:\\Users\\Administrator\\Desktop\\test.txt","C:\\Users\\Administrator\\Desktop\\test.zip");}}


运行结果:(.zip不能用WRAR打开,只是显示图标和.zip文件是一样的;.zip文件用的其他压缩方法)



五、总结:

1、明白了压缩的基本原理

2、知道如何利用哈夫曼树进行文件压缩

3、对对应索引位置的理解;


1 0
原创粉丝点击