基于哈夫曼二叉树的文件压缩实现
上一篇博客中我们提到如何将一个字符串建立哈夫曼二叉树
那么我们实现文件压缩其实已经成功了一半
哈夫曼二叉树可以得到哈夫曼编码,哈夫曼编码具有不重复的特性,我们可以利用这一特性来实现压缩
获取哈夫曼编码的规则是:从根节点出发往左就+“0”,往右就+“1”直到找到叶子节点
获取哈夫曼编码的方法:
/*
* 此方法用来得到哈夫曼编码
*/
public void getBM(Node roo,String st){
if((roo.getLeft()==null)&&(roo.getRight()==null)){
str+=st;
String q=str;
roo.getStr();
System.out.println(str);
}
if(roo.getLeft()!=null){
getBM(roo.getLeft(), st+"0");
}
if(roo.getRight()!=null){
getBM(roo.getRight(), st+"1");
}
}
/*
* 此方法封装了getBM(Node roo,String st)用来得到哈夫曼编码
*/
public void getBM(){
getBM(root, str);
}
因为叶子节点都是存储的我们字符串中存在的字符,所以每个字符都能对应一个哈夫曼编码,我们只需要将字符串中的字符替换位哈夫曼编码,就能生成一组01串,然后将01串每隔8位存储为一个byte,就能大大的压缩存储空间
我们这时很大可能遇到最后一位8位01串不足8位时,将01串补0补足8位即可
/*
* 此方法用来将01字符串转成8位的byte进行存储
*
*/
public byte[] turn(String c){
int t;
byte[] co=new byte[c.length()/8+1];
//将能组成8位的01串转换成byte
for(int i=0,j=0;i<c.length()-c.length()%8;i+=8,j++){
String a=c.substring(i, i+8);
t=Integer.parseInt(a, 2);
co[j]=(byte)t;
}
//将末尾不够8位的01传先补0然后转成byte
if(c.length()%8!=0){
String b=c.substring(c.length()-c.length()%8, c.length());
for(int i=0;i<8-c.length()%8;i++)b+="0";
t=Integer.parseInt(b, 2);
co[c.length()/8]=(byte)t;
}
return co;
}
到了这一步我们的主要任务已经完成
然后就是实现将文件读取为字节,然后转成字符串进行建立哈夫曼二叉树,得到哈夫曼编码,将原字符串进行压缩
public void read(String fileName){
//创建流
try {
InputStream in=new FileInputStream(fileName);
//创建缓存区域
buffer=new byte[in.available()];
//读取文件字节
in.read(buffer);
//关闭流
in.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
建树的方法请参照上一篇博客
/*
* 此方法用来将字符串替换为哈夫曼编码的01串
*/
public StringBuilder turnto01(String str){
String ss="";
StringBuilder sstr=new StringBuilder();
for(int i=0;i<str.length();i++){
char ch=str.charAt(i);
ss+=ch;
for(int j=0;j<codelist.size();j++){
Code code=codelist.get(j);
if(code.getName().equals(ss)){
sstr.append(code.getHuffman());
System.out.println(code.getHuffman());
ss="";
}
}
}
System.out.println("=========="+sstr);
return sstr;
}
在将我们压缩得到的byte数组写入文件前,我们必须还要考虑到一件事情,就是我们压缩文件是用来存储和传输的必须要考虑解压的问题,也就是将我们得到的码表也存储进文件
我这里呢是参考的江英同学的想法,先将总码表的长度存入-——》然后存入总码表——》然后将第一个字符的编码长度存入——》然后存储第一个的哈夫曼编码— 以此类推…存完码表之后我们还需要将压缩字符串的补零个数存入,方便解压——》最后我们存入压缩后的byte[]
写的比较匆忙注释写的比较少,如有问题可以参考下面的代码。
public ArrayList<Byte> getMB(Node ro) {
ArrayList<Byte> b = new ArrayList<Byte>();
int a = ro.getStr().getBytes().length;// 得到编码串的长度mavkl
byte[] by = inttobyte(a);
for (int i = 0; i < by.length; i++) {
b.add(by[i]);
}
System.out.println("字节数组大小:"+b.size());
byte[] byt = ro.getStr().getBytes();// 得到编码串mavkl并转成字节数组
// System.out.println("byt:"+byt.length);
for (int i = 0; i < byt.length; i++) {
b.add(byt[i]);
}
System.out.println("字节数组大小:"+b.size());
for (int i = 0; i < codelist.size(); i++) {
Code code = codelist.get(i);
int c = code.getHuffman().getBytes().length;
byte[] bbb = inttobyte(c);// 得到哈夫曼编码的内容长度
System.out.println("哈夫曼编码字节长度:"+c);
for (int j = 0; j < bbb.length; j++) {
b.add(bbb[j]);
}
System.out.println("字节数组大小:"+b.size());
byte[] yyy = code.getHuffman().getBytes();// 得到哈夫曼编码
for (int j = 0; j < yyy.length; j++) {
b.add(yyy[j]);
}
System.out.println("字节数组大小:"+b.size());
}
byte[] byy = inttobyte(y);//存储补零个数
for (int i = 0; i < byy.length; i++) {
b.add(byy[i]);
}
System.out.println("字节数组大小:"+b.size());
return b;
}
最后,我们读取压缩后的文件时可以按照相反的逻辑进行解压。