技术开发 频道

哈夫曼树和压缩算法

  举个例子,我们假设byte[] changeByte(byte oBt)方法就是将原byte转换为用byte[]表示的二进制位,注意:此方法不完善!

/**
*将二进制位转换为byte方法
*oBytes 组成源文件的byte[]
*return:转换后的Byte队列
*/
public byte[] compressor(byte[] oBytes){
    
byte[] bits = new byte[8];//储存8位二进制位
    List<Byte> bL = new ArrayList<Byte>();//转换后长度未知,先用list储存
    int bitsLeft = 8;//储存当前bits中剩余位数
    for(byte b:oBytes){
        
byte[] nBits = changeByte(b);//转码,得到二进制位
        for(byte oneBit:nBits){
            bits[
8-bitsLeft]=oneBit;//将新二进制位加入到当前byte的二进制位中
            bitsLeft--;//当前byte剩余位数-1
            if(bitsLeft==0){//当当前byte填满的时候
                try{
                
byte nByte = changeToByte();
                bL.add(nByte);
                bitsLeft
=8;
                }
catch(Exception ef){}
            }
        }
    }
}
/**
* 将8位二进制位转换为byte方法,此处用的是位运算
* bits : 储存8个二进制位的byte[]
* return : 转换成的byte
* throws:bits不是8个二进制位格式:不是8位或不止0和1
*/
public byte changeToByte throws Exception(byte[] bits){
    
if(bits.length!=8){//排除非8位数组的异常
        throw new Exception("不是8位!");
    }
    
byte b=0;
    
for(int i = 0;i<8;i++){
        
if(bits[i]!=0 && bits[i]!=1){//排除值不为0或1的异常
            throw new Exception("值不是0或1")
        }
        b
=(byte)(b<<1);//左移1位
        b+=bits[i];
    }
    
return b;
}

  相信有些人又发现问题了,最后一个byte编完的时候很可能出现没有补满的情况,那么最后一位怎么填满就成了第二个比较重要的问题了,弄不好就会出现解压时候多了个byte的情况,当然解决的方法也有很多,可以在最前面添加一个参数表示最后一位的空位数然后填满,也可以填入一个编码表中不存在的编码,我采用的是后者,可以节省1个byte的说明。

  这样下来,一个文件算是压缩完了,然而不能只压缩不解压,要解压的话,我们需要把编码表存入文件。由于文件长度未知,一般可以保存在文件头部。当然,编码表和正文部分需要用标记来区分开的,我自己是计算出编码表长度在开头用个int值注明,然后读取int个字节作为编码表,后面的就是正文了。当然还有别的方法,就看各位看官自己探索了~~~

  下面就是解压了,由于哈夫曼树生成的编码长度的不同,压缩过程中我们需要把8个bit转换为一个byte,c过程就会需要把一个byte拆分为8个bit,方法同样有很多,我这里提供一种方法:

/**
* 将一个byte的bit拆分方法
* b: 需要拆分的byte
* return:一个长度为8的byte[],每个元素储存一位bit
*/
public byte[] getBits(byte b){
    
byte[] bits = new byte[8];//定义用于储存bit的byte[]
    for(int i=0;i<8;i++){
        
byte t = (byte)(b<<i);//左移i位
        t = (byte)(t>>7)//再右移7位,这样最低位就是从高位开始第一位了
        t = (byte)(Math.abs(t));//避免负数的影响,主要是当该位为1时
        bits[i] = t;//赋值
    }
    
return bits;
}

  特别注意的是,将bit[]合并为byte和将byte拆分为bit[]的过程千万不要将byte的高低位弄颠倒!我就犯过这个错误。。找了半天才找到问题。有了这个方法,我们就可以做出解压动作了,将文件中每个byte依次读出(前面的编码表不要再读进来了),将byte拆分为bit[],然后一位一位的对照,就是先取bit[]第一位,查看编码表,没有再取前2位对照,如此反复,找到对应的就储存下来,一个byte读完了读下一个,一直到结束,这个文件就被解压完成了。

  后面的扩展就靠各位看官了,比如如何提高压缩/解压效率、压缩多个文件如何实现等,另外,如果修改下这个算法,各byte加权不是由出现次数来定而是事先约定,压缩的时候不将编码表存入文件,那么我们做出来的就是一个加密程序~~

0
相关文章