基于哈夫曼二叉树的文件压缩实现

基于哈夫曼二叉树的文件压缩实现

上一篇博客中我们提到如何将一个字符串建立哈夫曼二叉树

那么我们实现文件压缩其实已经成功了一半

哈夫曼二叉树可以得到哈夫曼编码,哈夫曼编码具有不重复的特性,我们可以利用这一特性来实现压缩

获取哈夫曼编码的规则是:从根节点出发往左就+“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;
	}

最后,我们读取压缩后的文件时可以按照相反的逻辑进行解压。

猜你喜欢

转载自blog.csdn.net/qq_41819698/article/details/82428998