`
御天田
  • 浏览: 15418 次
  • 性别: Icon_minigender_2
文章分类
社区版块
存档分类

我的《哈夫曼压缩》

阅读更多

《哈夫曼压缩》总结

压缩:
一:压缩原理
什么是哈夫曼压缩?呵呵。。。。顾名思义。借用哈夫曼树来实现的压缩就是的了。那么它的压缩原理是什么呢?(这里只能压缩和解压字符)
压缩:
首先将文件中每个字符出现的频率统计出来----->根据频率构建一颗哈夫曼树-------->根据构建的哈夫曼树获得每个字符的哈夫曼编码并存放在一个队列中(这里的每个字符实际上就是树的每个叶子节点,因为文件中出现的字符只会是树的叶子节点)------>写入码表和源文件
码表:码表中应包含编码的个数、每个编码的补零个数、码表的长度、字符的ascll码值、字符对应的哈夫曼编码补足零后的编码
源文件:挨个读出源文件中的字节(此时读出的是字符对应的ascll码),再将读出的ascll码和之前写入的码表中的ascll码相比对,找到相等的值后再写入在码表中该ascll码对应的哈夫曼编码值,注意在写入最后一个字节时,要判断是否需要补零,以及补零个数,便于文件的解压

二:压缩算法分析
/**
	 * 根据文件统计文件中每个字节出现的频率
	 * 
	 * @param path
	 *            文件路径
	 */
	// 创建一个存放字节频率的int数组
	private int arrByte[] = new int[256];

	public int[] countByte(String path, Probar pro) {
		int newarr[] = new int[256];
		// 创建输出流对象
		try {
			FileInputStream fis = new FileInputStream(path);
			filelen = fis.available();

			// pro=new Probar("压缩");
			// pro.start();
			while (fis.available() > 0) {// 当文件中还有字节
				// System.out.println("|||||||||||||||||||"+prolen);
				float f = (float) ((float) filelen - (float) fis.available())
						/ (float) filelen;
				// System.out.println("@@@@@@@@@@@@@@@@@@filelen=" + filelen
				// + "   还剩字节数:" + fis.available() + "此时的prolen=" + prolen
				// + "  f=" + f);
				if (f * 100 > prolen) {
					// System.out.println("开始prolen的转变了!!!");
					prolen = (int) (f * 100);
					// System.out.println("()()()()()()()()f*100="+f*100);
					// System.out.println("转变后的prolen是:"+prolen);
					pro.getBar().setValue(prolen);
				}
				int i = fis.read();
				arrByte[i]++;
			}

			newarr = arrByte;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return newarr;
	}


/**
	 * 将表头和源文件写入压缩文件。实现压缩
	 */
	public int yasuo(ArrayList<TreeNode> list) {
		// 写表头
		// 写入码表长度
		int length = 0;
		for (int j = 0; j < list.size(); j++) {
			// System.out.println(list.get(j).getHfmcode()+"写入码表时每个的哈夫曼编码的长度是:"+list.get(j).getHfmcode().length());
			length += 3 + list.get(j).getHfmcode().length();
		}
		// System.out.println("写入文件时码表的长度是:"+length);
		try {
			out.write(length);
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		for (int i = 0; i < list.size(); i++) {
			TreeNode node = list.get(i);
			// System.out.println(node.getASCLL()+"哈夫曼编码"+node.getHfmcode());
			// System.out.println("补零个数"+node.getCount_0());
			// System.out.println("补零后哈夫曼编码"+node.getHfmcode_0());
			try {
				// 写入字符对应的阿斯科码值
				out.write(node.getASCLL());
				// System.out.println("已经写入了ascll:" + node.getASCLL());
				// 写入补零后的编码字节数
				out.write(node.getHfmcode_0().length() / 8);
				// 写入每个编码补零的个数
				out.write(node.getCount_0());
				int k = node.getHfmcode_0().length();
				// 如果补零后的编码长度大于8
				if (k > 8) {
					int t = 0;
					while (t < k) {
						String str = "";
						for (int j = t; j < t + 8; j++) {
							// System.out.println(j+"---------");
							str += node.getHfmcode_0().charAt(j);
						}
						// System.out.println("写入的补零后8位编码是"+str);
						out.write(changeString(str));
						t += 8;
					}
				} else {
					out.write(changeString(node.getHfmcode_0()));
				}

			} catch (IOException e) {
				e.printStackTrace();
			}

		}

		/**
		 * 开始写入源文件
		 */
		try {
			// 创建一个输入流对象
			FileInputStream fis = new FileInputStream(path);
			// 开始时间条进程

			// FileOutputStream fos=new FileOutputStream("src/ya_txt");
			// 源文件中字符匹配到的哈夫曼编码,也就是要输入文件的哈夫曼编码
			String hfm = "";
			// 从源文件中读取的字符的ascll码
			int ascll;
			try {

				// 当文件还有字节的时候
				while (fis.available() > 0) {
					ascll = fis.read();
					// write是要写入的编码
					String write = "";
					for (int i = 0; i < list.size(); i++) {
						// 如果检索到对应的ASCLL
						if (ascll == list.get(i).getASCLL()) {
							// 将哈夫曼编码串起来
							hfm += list.get(i).getHfmcode();
							// 如果哈夫曼编码大于8位
							if (hfm.length() > 8) {
								// 先取前八位
								for (int j = 0; j < 8; j++) {
									write += hfm.charAt(j);
								}
								// 将前八位写入文件中
								out.write(changeString(write));
								// 清空前八位,继续读取后面不足八位的哈夫曼编码
								String newWrite = "";
								for (int k = 8; k < hfm.length(); k++) {
									newWrite += hfm.charAt(k);
								}
								hfm = newWrite;
							} else {// 如果哈夫曼编码不足8位
								int newascll = fis.read();
								for (int l = 0; l < list.size(); l++) {
									if (newascll == list.get(l).getASCLL()) {
										// 找到对应的哈夫曼编码并取出
										hfm += list.get(l).getHfmcode();
									}
								}
							}
						}
					}
				}
				// 判断文件是否需要补零
				if (hfm.length() % 8 != 0) {
					File_0 = 8 - hfm.length() % 8;
				}
				// 写入文件补零个数
				// out.write(File_0);
				for (int i = 0; i < File_0; i++) {
					hfm += "0";
				}
				if (hfm != null) {
					// System.out.println("file_0="+File_0+"<><><><><><><><><>hfm"+hfm);
					out.write(changeString(hfm));
				}
				// 关闭输入输出流
				fis.close();
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		return File_0;
	}



解压:
一:解压原理
写完压缩,解压其实很简单,相当于解压的一个逆运算,首先
读码表:根据写入码表时各个元素的顺序读出相应的编码的个数、每个编码的补零个数、码表的长度、字符的ascll码值、字符对应的哈夫曼编码补足零后的编码,将读出的ascll码以及相应的哈夫曼编码(这里的哈夫曼编码是去掉补零后的原始编码)存放在一个hashmap中,编码作为key、ascll值作为键值value以便于后面对读取的源文件进行还原。
读源文件:先将读取的字节转换成一个01串,再从头逐个读取01串将其作为map的键值在之前码表的hashmap中看是否存在这个键值。若存在则取出键值写入目标文件中并设置下次读取从这个0/1串后一位开始。若不存在,则再往后读一个0/1字符继续进行比较。这样循环比较直到最后一个字节,因为最后一个字节中包含写入文件时文件是否补零以及补零个数,所以应该根据压缩时写入文件最后一个字节的元素顺序读出补零个数,再根据补零个数取出真正的编码,并在map中利用前面的方法找到对应的ascll码并写入目标文件中。

二:解压算法分析

/**
	 * 1.读取文件中的码表
	 * 2.取出码表中的ascll码和对应的哈夫曼编码,存放在hashmap中
	 * 3.读取文件的源文件内容,注意要去掉写入源文件时最后的补零
	 * 4.将读取源文件的哈夫曼编码逐一的跟hashmap中的哈夫曼编码比对,若比对相同,则将该哈夫曼编码对应的
	 * ascll码翻译成字符并写入到新文件中
	 */
public void jieya(ArrayList<TreeNode> list,JTable table){
		try {
			fis=new FileInputStream(FilePath);
			fos=new FileOutputStream(newPath);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		map=new HashMap<String,Integer>();
		//读取码表长度
		try {
			length=fis.read();
			System.out.println("码表的长度是:"+length);
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		while(length>0){
			try {
				//读取码表中的ascll码
				int ascll = fis.read();
				System.out.println("读到的ascll码是:"+ascll);
				//读取码表中每个ascll码对应的哈夫曼码字节数
				int count_hfm=fis.read();
				System.out.println("它对应的哈夫曼共"+count_hfm+"个字节");
				//读取哈夫曼编码补零的个数
				int count_hfm_0=fis.read();
				System.out.println("该哈夫曼编码中补零的个数是:"+count_hfm_0);
				//读取码表中对应的哈夫曼编码
				String hfmCode_0="";
				for(int j=0;j<count_hfm;j++){
					 hfmCode_0+=int_to_string(fis.read());
				}
				System.out.println("它对应的哈夫曼编码是:"+hfmCode_0);
				String hfmCode="";
				for(int k=0;k<hfmCode_0.length()-count_hfm_0;k++){
					hfmCode+=hfmCode_0.charAt(k);
				}
				System.out.println("它对应的去掉零后的哈夫曼编码是:"+hfmCode);
				//将码表中的ascll码及其对应的哈夫曼编码写入map中
				map.put( hfmCode,ascll);
				System.out.println("存放在map中的hfmCode="+hfmCode+"   对应的ascll="+map.get(hfmCode));
//				length-=(3+count_hfm_0);
				length-=(3+hfmCode.length());
				System.out.println("此时码表的长度是:"+length);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		/**
		 * 读取源文件
		 */
		String file="";
		try {
			while(fis.available()>1){
				int write=0;
				//将读取的字节转换成01串
				file+=int_to_string(fis.read());
				System.out.println("%%%%%%%%%%%%%%读取到的哈夫曼编码是:"+file);
				int count=1;
				int k=0;
				while(count<=file.length()){
					String hfm="";
					for(int i=k;i<count;i++){
						hfm+=file.charAt(i);
//						System.out.println("count="+count+"   i="+i+"&&&&&&&&&&&&&&hfm="+hfm);
					//	System.out.println("((((((((((((map中:"+map.get(hfm));
						if(map.get(hfm)!=null){
							System.out.println("*******************找到对应的哈夫曼编码是:"+map.get(hfm));
							fos.write(map.get(hfm));
							write+=hfm.length();
							k=count;
						}
					}
					count++;
				}
				String str="";
				for(int i=write;i<file.length();i++){
					str+=file.charAt(i);
				}
				file=str;
			}
			//读取最后一个字符
//			CreateTree ct=new CreateTree();
			while(fis.available()>0){
				String str_end=file+int_to_string(fis.read());
				System.out.println("{}{}{}{}{}{}{}{}{}{}最后一个字符是:"+str_end);
				//读取源文件补零的个数
				int count_file_0=File_0;
				System.out.println("++++++++++++补零的个数是:"+count_file_0);
				String str_0="";
				for(int i=0;i<str_end.length()-count_file_0;i++){
					str_0+=str_end.charAt(i);
				}
				str_end=str_0;
				System.out.println("[][][][][][][][][][]去掉补零后的哈夫曼编码是:"+str_end);
				int count=1;
				int k=0;
				while(count<=str_end.length()){
					String str="";
					for(int i=k;i<count;i++){
						str+=str_end.charAt(i);
					}
					if(map.get(str)!=null){
						//写入最后一个字符
						fos.write(map.get(str));
						System.out.println("写入的最后一个01串中对应的ascll是:"+map.get(str));
						k=count;
					}
					count++;
				}
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}


附图:




小女不才,可能大家看了我的文字后还是搞不清所以然,下面附上了源代码,希望对大家能有所帮助。欢迎大家提出宝贵意见哈!
  • 大小: 34.5 KB
分享到:
评论

相关推荐

    vc++哈夫曼压缩算法

    vc++哈夫曼压缩算法 vc++哈夫曼压缩算法

    哈夫曼压缩解压算法-C语言

    mypage文件可能包含了实现哈夫曼压缩和解压缩算法的C源代码文件,以及相关的测试数据或结果。通过阅读和理解这些代码,你可以深入学习哈夫曼编码的工作原理,以及如何在C语言中实现这一算法。同时,还可以了解到如何...

    哈夫曼压缩与解压缩源码.zip

    哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩源码.zip哈夫曼压缩与解压缩...

    哈夫曼压缩解压缩

    总之,哈夫曼压缩解压缩是数据压缩领域的一个重要方法,通过MFC和VC++,我们可以构建出直观且高效的压缩工具,便于学习和研究。在这个过程中,理解哈夫曼编码的原理、掌握MFC的使用以及实现压缩和解压缩的算法,都是...

    哈夫曼压缩

    ### 哈夫曼压缩详解 #### 一、哈夫曼压缩概述 哈夫曼压缩是一种广泛应用于数据压缩领域的无损压缩技术。无损压缩意味着压缩后的数据在解压后可以完全恢复到原始状态,不丢失任何信息。哈夫曼编码通过构建一个特殊...

    java哈夫曼压缩和解压

    在Java中实现哈夫曼压缩和解压涉及到以下几个关键知识点: 1. **哈夫曼树**: 哈夫曼树(Huffman Tree)是一种带权路径长度最短的二叉树,也称为最优二叉树。它通过将频率较低的字符编码为较短的位序列,而频率较...

    哈夫曼压缩与解压文件课程设计(代码+实习报告)

    哈夫曼编码是一种高效的数据压缩方法,由大卫·哈夫曼在1952年提出。它是基于一种称为哈夫曼树(也叫最优二叉树)的数据结构。在这个课程设计中,你将深入理解哈夫曼编码的原理,并通过C++编程语言实现文件的压缩与...

    哈夫曼压缩与解压缩设计

    哈夫曼压缩是一种高效的数据压缩方法,它基于字符出现频率构建一种特殊的二叉树——哈夫曼树。在计算机科学中,尤其是信息处理和文件压缩领域,哈夫曼编码是广泛应用的技术之一。ASC II码是计算机中用8位二进制数...

    java实现哈夫曼压缩的实例

    在Java中实现哈夫曼压缩涉及到的主要步骤包括统计字节频率、构建哈夫曼树以及生成哈夫曼编码。首先,我们需要创建一个字节类(`NodeData`)来表示每个字节及其对应的权重(频率)。下面我们将详细讲解这些步骤: 1....

    huff.rar_MATLAB 图片压缩_huff_哈夫曼 压缩_哈夫曼压缩_图片压缩matlab

    哈弗曼编码(Huffman Coding)是一种数据压缩算法,由美国计算机科学家大卫·艾尔文·哈弗曼在1952年提出。这个算法基于一种称为“最优二叉树”(也称哈弗曼树)的数据结构,主要用于对频率不同的字符进行编码,从而...

    哈夫曼压缩算法

    哈夫曼压缩算法,全称为哈夫曼编码(Huffman Coding),是一种高效的无损数据压缩方法,由美国科学家大卫·艾尔·哈夫曼在1952年提出。它是基于字符频率(权重)构建最优二叉树的思想,通过创建一棵特殊的二叉树——...

    哈夫曼压缩与解压缩程序(JAVA)

    在Java编程环境中实现哈夫曼压缩与解压缩程序,我们可以利用面向对象的特性,设计多个类来完成不同部分的功能。 1. **FileChooserDemo.java**:这是一个用户界面类,通常包含用于让用户选择输入和输出文件的控件。...

    哈夫曼压缩与解压缩

    哈夫曼实现文件的压缩与解压缩,代码关键部分有注释。

    哈夫曼压缩.zip

    在"哈夫曼压缩.zip"这个压缩包中,我们可以预见到包含了一个与哈夫曼压缩相关的项目。这个项目可能是用Visual Studio(VS)开发环境编写的,这意味着它可能是一个C++、C#或Visual Basic等编程语言的实现。实习性质...

    哈夫曼压缩图片.rar

    每次选出权值最小且没有双亲的两个节点建立新的哈弗曼树。 无栈非递归遍历Huffman树,求Huffman编码。...要注意的是当文件较小时,不宜使用哈夫曼来进行压缩,此时文件头占比过大,会使压缩结果很差。

    哈夫曼压缩和解压c++(源码)

    在C++中实现哈夫曼压缩和解压,主要涉及到数据结构(如优先队列、二叉树)和文件操作(读写)。`huffmain`可能是这个C++项目的主程序文件,其中可能包含了构建哈夫曼树、生成编码、压缩和解压等核心功能的实现。具体...

    java哈夫曼压缩

    在Java中实现哈夫曼压缩通常包括以下几个关键步骤: 1. **构建哈夫曼树**:首先,需要统计输入文本中每个字符出现的频率。然后,根据这些频率创建一个哈夫曼树。哈夫曼树是一种特殊的二叉树,其特点是叶子节点代表...

    哈夫曼压缩——GUI

    哈夫曼压缩是一种高效的数据编码方法,主要用于无损数据压缩,尤其在文本、图像和音频文件的压缩中广泛应用。这个项目“哈夫曼压缩——GUI”是基于Visual Studio 2010开发的一个图形化界面工具,对于学习数据结构和...

    c++编写的哈夫曼压缩软件

    在C++中实现哈夫曼压缩软件,我们需要理解以下几个核心概念和技术: 1. **哈夫曼树**:哈夫曼树是一种带权路径长度最短的二叉树,也称为最优二叉树。构建哈夫曼树的过程是通过合并频度最低的两个节点来逐渐构建整个...

Global site tag (gtag.js) - Google Analytics