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


 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的权值,那么我们得到的哈夫曼树如下图:

Java小程序之哈夫曼树与文件压缩和解压缩(二)文件压缩篇_第1张图片



 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字母对应的哈夫曼编码

Java小程序之哈夫曼树与文件压缩和解压缩(二)文件压缩篇_第2张图片

 

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

Java小程序之哈夫曼树与文件压缩和解压缩(二)文件压缩篇_第3张图片


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

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

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 list = new LinkedList();
	//统计次数
	
	//初始化
	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){
		//往左走,哈夫曼编码加0
		if(root.getLeft()!=null){
			getHuffmCode(root.getLeft(),code+"0");
		}
		//往右走,哈夫曼编码加1
		if(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 

测试类:

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文件用的其他压缩方法)

Java小程序之哈夫曼树与文件压缩和解压缩(二)文件压缩篇_第4张图片


五、总结:

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

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

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


你可能感兴趣的:(Java小程序,Java小程序开发)