哈弗曼压缩的原理在这就不多说了,相信大家都了解(不了解的看有关资料),在这只讨论一下哈弗曼压缩步骤:
哈夫曼压缩:
1.读文件,统计文件中每个字节出现的次数,并用一个数组来保存
2.把数组中的每一个下标作为节点的元素(即为读取到的字节),下标对应的元素作为节点元素出现的次数, 构造节点优先队列
3.根据节点优先队列构建哈树
4.得到每个叶节点(即为字节)的编码
5.读文件,得到有序字符串
6.将有序字符串保存到字节数组中
7.将字节数组中的字节写入到文件当中
压缩:例如,获得了文件中的所有字节的字符串
1.如文件中:abbcccddddeeeee 假设文件是a.txt;
2.读文件,将读到的字节保存到一个int[]数组中
3.创建一个优先级队列将数组中的元素进行排序。
4.获得哈夫曼树的编码为:(且将编码 存到一个字符串数组当中即strs[256])
a="010";--------strs[97]="010";
b="011";--------strs[98]="011";
c="00";---------strs[99]="00";
d="10";---------strs[100]="10";
e="11";---------strs[101]="11";
5.再次读文件得到文件中所有字节的有序编码字符串
allString="010 011011 000000 10101010 111111111111";
6.将allString字符串每8位每8位保存到一个字节数组byte[] 当中
如: byte[0]=77;
byte[1]=-127;
byte[2]=85;
byte[3]=-1;
byte[4]=-128;
byte[5]=7;
7.写文件,把字节数组写入到文件当中 假设写入的文件是b.txt;
----------就完成压缩了!!!
以下是压缩代码:
public class YaSuo {
/**
* 步骤一: 读取文件,统计文件中每个字节出现的次数,将字节出现的次数用一个int[] num = new int[256]保存
* 数组的下标表示字节,下标对应的元素表示该字节出现的次数
*
* @param path
* 要读取的文件路径
* @return 返回用来存储字节次数的数组
*/
public int[] readFile(String path) {
// 创建一个数组用来保存读到的字节 下标i 0-255是所有的字节 t表示每个字节出现的频率
int[] num = new int[256];
try {
// 创建文件输入流用来读文件
FileInputStream fis = new FileInputStream(path);// 此处抛出异常
// 把输入流包装成缓冲流
BufferedInputStream bis = new BufferedInputStream(fis);
// 用缓冲流读字节,并把读到的字节保存到数组当中以便获得每个字节的频率
while (bis.available() > 0) {// 当剩余字节不为0时
int t = bis.read();// 缓冲流读到不同的字节就会返回不同的整型即0-255
num[t]++;// 当读到相同的字节时频率就加1
// 將獨到的字節設置成節點元素
HfmNode node = new HfmNode(t, num[t]);
// System.out.println("節點元素:"+(Integer)node.obj);
}
} catch (Exception ef) {
ef.printStackTrace();
}
return num;
}
/**
* 步骤二: 当字节数组中元素不为0的时候,就创建一个树的节点对象, 将节点对象使用一个优先对象存储,来保证节点按照字节出现的次数排序
*
* @param arr
* 用来保存字节出现次数的数组
* @return 返回保存节点的优先队列
*/
public PriorityQueue<HfmNode> array2Queue(int[] arr) {
// 创建排序队列,自己制定比较器
PriorityQueue<HfmNode> nodeList = new PriorityQueue<HfmNode>(11,
new MyComparator());
for (int i = 0; i < arr.length; i++) {
if (arr[i] != 0) {// 如果出现的次数不是0,则创建节点
// 创建节点对象
HfmNode node = new HfmNode(i, arr[i]);
nodeList.add(node);
}
}
return nodeList;
}
// 实现比较器类
class MyComparator implements Comparator<HfmNode> {// 按频率的大小进行比较
public int compare(HfmNode h1, HfmNode h2) {
return h1.getCount() - h2.getCount();
}
}
/**
* 将优先级队列中的节点创建成哈夫曼树
*
* @param nodeList
* 优先级队列
* @return 返回根节点
*/
public HfmNode createHfmTree(PriorityQueue<HfmNode> nodeList) {
while (nodeList.size() > 1) {// 当优先级队列当中至少有两个节点时
// 取出两个最小的节点
HfmNode node1 = nodeList.poll();
HfmNode node2 = nodeList.poll();
// 有最小的节点创建合并的节点
HfmNode addNode = new HfmNode(null, node1.getCount()
+ node2.getCount());
// 创建引用关系
addNode.setLeft(node1);
node1.setString("0");// 左节点编码为0
addNode.setRight(node2);
node2.setString("1");// 右节点编码为1
node1.setLast(addNode);
node2.setLast(addNode);
// 将合并的节点加到优先队列当中
nodeList.add(addNode);
}
HfmNode root = nodeList.peek();// 取出根结点
return root;
}
/**
* 得到每个叶节点即字节的哈夫曼编码
*
* 1.先得到每个叶节点的编码,将其保存在字符串数组中 如:String[] str=new String[256] 如str[97]="00011"
* str[98]="011111"
*
*
*
*/
public String[] createByteCode(HfmNode root) {
// 用一个字符串数组保存所有字符串编码如strs[97]="00011" str[98]="011111"
// 如果定義為屬性的話,創建類對象時數組也會創建,浪費內存,所以把其放在方法中
String[] strs = new String[256];
Byte2String(strs, root);
return strs;
}
private void Byte2String(String[] strs, HfmNode root) {
if (root != null) {// 如果根節點不為空
if (root.getLeft() != null) {// 如果左節點不為空,得到其編碼
HfmNode leftNode = root.getLeft();// 得到左節點
String str1 = root.getString() + leftNode.getString();// 左節點的編碼是父節點加自身節點
leftNode.setString(str1);// 重新設置左節點編碼
// System.out.println("獨到的字節"+(Integer) leftNode.getObject());
// 將左節點的字節和編碼保存到字符串數組當中
// 注意:是把葉節點的元素加進去,而不是所有創建樹的所有節點
if (leftNode.getObject() != null) {
// System.out.println("葉節點元素:"+(Integer)leftNode.getObject()+"葉節點字符串編碼:"
// +leftNode.getString());
strs[(Integer) leftNode.getObject()] = leftNode.getString();
}
// 遞歸調用
Byte2String(strs, leftNode);
}
if (root.getRight() != null) {// 右節點不為空
HfmNode rightNode = root.getRight();// 得到右節點
// System.out.println("右節點的編碼:"+rightNode.getString());
// 重新設置右節點編碼,即其編碼為父節點編碼加自身編碼
String str2 = root.getString() + rightNode.getString();
rightNode.setString(str2);
// 將右節點的字節和編碼保存到字符串中
if (rightNode.getObject() != null) {
// System.out.println("葉節點元素:"+(Integer)rightNode.getObject()+"葉節點字符串編碼:"
// +rightNode.getString());
strs[(Integer) rightNode.getObject()] = rightNode
.getString();
}
// 遞歸調用
Byte2String(strs, rightNode);
}
}
}
/**
* 需要再次读文件,将文件中的所有字节按顺序用字节对应的编码表示,得到字符串 String allString=""; 如
* allString="0000010010101111100110101001111";
*
* @param strs
* @return
*/
public String readAgain(String[] strs, String path) {
// 文件中所有字節按順序對應的編碼
String allString = "";
try {
// 创建文件输入流用来读文件
FileInputStream fis = new FileInputStream(path);// 此处抛出异常
// 把输入流包装成缓冲流
BufferedInputStream bis = new BufferedInputStream(fis);
// 用缓冲流读字节,并把读到的字节保存到数组当中以便获得每个字节的频率
while (bis.available() > 0) {// 当剩余字节不为0时
int t = bis.read();// 缓冲流读到不同的字节就会返回不同的整型即0-255
// 存放从文件中读到的所有有序的字节编码
allString = allString + strs[t];
}
} catch (Exception e) {
e.printStackTrace();
}
return allString;
}
/**
* 将有序的字符串转化成字节
*
* @param allString
* 所读的文件中字节转化成的字符串
* @return 返回转化的字节
*/
// 将有序的字符串保存到数组当中
public byte[] toByte1(String allString){
//先定义字节数组的长度
int len=allString.length()/8+1;
if(allString.length()%8!=0){
len++;
}
//创建字节数组
byte[] b=new byte[len];
//判断字节数组最后一位的字节
if(allString.length()%8==0){
b[len-1]=0;
}else{
int length=8-allString.length()%8;
b[len-1]=(byte) length;
int buling=8-allString.length()%8;
for(int i=0;i<buling;i++){
allString=allString+"0";
}
}
// 将字符串转化成字节保存到字节数组当中
for (int i = 0; i < len-1; i++) {
String duanString = allString.substring(i * 8, i * 8 + 8);
b[i] = (byte)(Integer.parseInt(duanString, 2));
//System.out.println(duanString+"<>"+b[i]);
}
//System.out.println("字节数组的长度"+b.length);
return b;
}
/**
* 写入文件的方法 输出流
* @param path 写入的文件路径
* @param bs 要写入的数据
*/
public static void writeFile(String path,byte[] bs){
//方法分析:在写文件方法中,例如,在记事本上写数据(是字节),是从内存中将数据流出到记事本中
//步骤:1.建立一个文件输出流对象,指明要输出到的文件路径,相当于一个管道,连接内存和写的文件
// 即java.io.FileOutputStream fos=new java.io.FileOutputStream(path
//2.将内存中的数据写出来,因为事先在方法中已经定义路径和写的数据是字节数组类型
// 即//for(int i=0;i<bs.length;i++){
// byte b=bs[i];
// //写出字节
// //问题write(byte[] b)
// //有的write()方法,并不是一个节一个字节的写出,而是存在缓存区中,当满了就输出
// fos.write(b);
// }
//3.将缓冲区中的数据清空及关闭输出流fos.flush();fos.close()
try{
//创建文件输出流
java.io.FileOutputStream fos=new java.io.FileOutputStream(path);
//将字符串转化成字节数组
//byte[] bs=content.getBytes();
for(int i=0;i<bs.length;i++){
byte b=bs[i];
//写出字节
//问题write(byte[] b)
//有的write()方法,并不是一个字节一个字节的写出,而是存在缓存区中,当满了就输出
fos.write(b);
}
//清空数据(内存缓冲区的)
fos.flush();
//关闭输出流
fos.close();
}catch(Exception df){
df.printStackTrace();
}
}
/**
* 步骤七:将有序字节数组中的字节写入到文件当中
* @param path 写入的文件路径
* @param b 字节数组: 写入的字节
*/
public void writeFile1(String path,byte[] b){
try{
//创建文件输出流
FileOutputStream fos=new FileOutputStream(path);
BufferedOutputStream bos=new BufferedOutputStream(fos);
//遍历字节数组中的字节
//有的write()方法,并不是一个字节一个字节的写出,而是存在缓存区中,当满了就输出
for(int i=0;i<b.length;i++){
bos.write(b[i]);
}
bos.flush();//清空的是缓冲流中的东西,而不是输出流中的东西。注意啊!!!!!!!!!!!
fos.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
感悟:老师给我们讲了哈弗曼压缩原理,让自己写一个压缩软件,一开始不知道怎么入手,一点思路都没有,在老师的耐心指导下, 才真正理解哈弗曼压缩原理,在方法上也要遵循每写一个方法都要测试一下正确性,否则到最后一起测试的时候,找不出到底是哪个方法出错了!
分享到:
相关推荐
哈弗曼压缩是一种高效的数据压缩方法,主要基于哈弗曼树(Huffman Tree)的构建。这个技术在信息编码和数据存储中具有广泛的应用,特别是在文本压缩、图像压缩和文件传输等领域。以下是对哈弗曼压缩相关知识点的详细...
在哈弗曼压缩过程中,首先需要统计输入文本中各个字符的出现频率,然后构建哈弗曼树。构建过程通常采用贪心算法,通过不断地合并频率最小的两个节点来逐步形成完整的树形结构。最后,根据树的结构生成每个字符的...
在哈弗曼压缩过程中,首先需要建立一个哈弗曼树。这通常包括以下步骤: 1. **统计字符频率**:对输入的数据流进行分析,统计每个字符出现的次数。 2. **构建哈弗曼树**:创建一个空的优先队列,将每个字符作为一个带...
哈弗曼压缩编码
在Java环境中实现哈弗曼压缩与解压文件,主要涉及以下几个关键知识点: 1. **哈弗曼树的构建**:哈弗曼树是一种带权路径长度最短的二叉树,权重小的节点通常位于树的顶层。构建哈弗曼树通常使用优先队列(如Java中...
这个"自适应哈弗曼压缩编码VC源码"是使用Visual Studio 2010开发的一个工程,它提供了实现自适应哈弗曼编码算法的C++源代码。VS2010是一个流行的集成开发环境(IDE),支持C++编程,提供了一整套工具,包括编译器、...
在C语言实现哈弗曼压缩的过程中,首先需要做的是统计输入文本中每个字符的出现频率。这个过程通常涉及读取文本文件,计算每个字符的词频,并将这些信息存储在一个字典或数组中。在这个案例中,`词频表_Character.txt...
哈弗曼编码(Huffman Coding)是一种数据压缩算法,由美国计算机科学家大卫·艾尔曼在1952年提出。...在实际项目中,哈弗曼压缩算法常用于文本、图像和其他类型数据的压缩,以节省存储空间和提高传输效率。
在给定的文件中,我们有两个文件:一个是关于MATLAB实现的哈弗曼压缩纯英文文本的程序,另一个是关于图像的哈弗曼编码。MATLAB是一种强大的编程环境,尤其适用于数值计算和算法开发,因此它是实现哈弗曼编码的理想...
实现哈弗曼压缩及解压缩功能,并计算压缩前后文件占用空间比 模型建立与题目分析 压缩: 以二机制可读形式打开源文件,以二进制可写形式打开压缩目标文件。每次从源文件读取八个比特(一字节),作为一个ASCII码...
在这个“数据结构哈弗曼压缩课设”中,你需要理解和应用以下几个关键知识点: 1. ASCII码:ASCII码(American Standard Code for Information Interchange)是计算机中字符的一种标准编码方式,包括数字、字母和...
huffmanCompress哈弗曼压缩与解压缩,一个压缩工具
### 哈弗曼压缩编码知识点详解 #### 实验项目名称 - **哈夫曼编码/译码器** #### 实验目的 本实验的主要目的是实现一个哈夫曼编码与解码的过程,即对输入的字符串进行哈夫曼编码,再对编码后的字符串进行解码,...
在这个“课程实习时做的哈弗曼压缩程序”中,我们可以推测作者尝试实现了哈弗曼编码的基本流程,包括以下几个步骤: 1. **频率统计**:首先,需要对输入的文本或数据流中的字符进行频率统计,了解每个字符出现的...
《哈弗曼压缩软件》是基于数据结构课程设计的一个项目,主要目的是让学生将所学的哈弗曼编码理论知识应用于实际问题中,提高编程技能。哈弗曼编码是一种高效的无损数据压缩方法,通过构建最优的二叉树(哈弗曼树)来...
哈弗曼压缩的过程包括以下步骤: 1. **构建哈弗曼树**:首先统计每个字符的出现频率,然后用这些频率作为权重创建一个优先队列。接着,每次从队列中取出两个权重最小的节点,合并成一个新的节点,新节点的权重为两子...
在VC(Visual C++)环境中实现哈弗曼压缩和解压,通常涉及到以下几个关键步骤: 1. **频率统计**:首先,我们需要对输入的数据流进行分析,统计每个字符出现的频率。这是构建哈弗曼树的基础。 2. **构建哈弗曼树**...
《哈弗曼压缩软件(数据结构试验报告附源程序).docx》的文件内容主要涉及一个数据结构课程设计项目,该项目是实现一个基于哈弗曼编码的文本压缩软件。哈弗曼编码是一种高效的前缀编码方法,常用于数据压缩,其原理是...
### 哈弗曼压缩原理详解 #### 引言 数据压缩技术自其诞生以来,一直是信息技术领域的重要组成部分,尤其在大数据和网络通信时代显得更为关键。哈弗曼压缩算法,由D.A. Huffman在1952年提出,是一种基于统计特性...
在哈弗曼压缩过程中,首先需要统计输入文本中每个字符的出现频率,生成一个频率表。然后,使用这些频率来构造一棵哈弗曼树,这是一个特殊的二叉树,其中每个叶子节点代表一个字符,而内部节点表示合并后的频率。构建...