`
旭冬冬
  • 浏览: 12807 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

HFM压缩总结

阅读更多
  哈夫曼压缩
做哈夫曼压缩最重要的是先要搞清楚每一步要做什么。。。步骤搞清楚之后才能开始下手
1, 先创建一个大小为256的整形数组,读取一个目标文件,将每个字节做数组下标,字节出现的次数作为数组值,这样就有了一个字节与其次数的对应关系;
2, 接下来再将每个字节的次数作为权值构造哈夫曼树,权值越大的越在下面,同时新建一个节点类,节点类存储字节,和字节的数目,字节编码,之后通过遍历哈夫曼树就能够给每一个字节一个特定的编码了。
3, 在编码完成之后,再读取一次原文件然后每读一个字节就会对应一个01串编码,每次都加起来,这样读取完成之后,就有一个很长的01字符串了,接下来就将01字符串8位8位的截取,每8位01串转成10进制,然后将码表与十进制数组存进文件中,这样就实现了压缩。
4, 而解压就是压缩的逆运算,怎么压的就怎么解压,这个很简单的,呵呵
代码如下:
public class HFMApp {
public ArrayList<Data>Strcodes=new ArrayList<Data>();//存储字节编码的队列
int Btcodes[]=new int[256];//存储字节数目的数组
int[]Intcodes;//存储由二进制转十进制之后的数组
byte addzero;//补0的个数
String Jiestr="";//解压时用到的字符串
/**********************************************************************************************
  * 压缩
  * ********************************************************************************************/
public void yasuo(String fpath,String npath){//压缩的方法fpath表示原文件路径,npath表示压缩完之后的路径
countBytes(fpath);//得到字节数组
PriorityQueue<TreeNode> queue=array2Queue(Btcodes);//排序,构造哈夫曼树
TreeNode root=queue2HFMTree(queue);//得到根节点
// System.out.println("根节点权值:"+root.nums);
getHFMCodeByTree(root);//得到叶子节点的HFM编码
Intcodes=CutString(Strcodes,fpath);//得到字符串代表的十进制数组
writeYaFile(Intcodes,npath);//写入文件v
System.out.println("压缩完毕");

}

/**
* 1.统计文件中每个字节出现的次数
* @param path
* @return
*/
public void countBytes(String path) {
// 数组的下标作为对应的字节,数组的元素是字节出现的次数
try {
// 建立文件输入流
FileInputStream fis = new FileInputStream(path);
// 包装成数据输入流
BufferedInputStream dis = new BufferedInputStream(fis);
// 读取一个字节
int by = dis.read();
while (by!=-1) {
// System.out.println("字节:"+by);
Btcodes[by]++;
// 读取下一个字节
by = dis.read();
}
// 关闭数据流
fis.close();

} catch (Exception ef) {
ef.printStackTrace();
}
}

/**
* 2.根据字节出现的次数作为权值构造结点对象,放入优先队列
*
* @param counts
*            字节次数的数组
* @return 返回优先队列
*/
public PriorityQueue<TreeNode> array2Queue(int[] counts) {

// 创建优先队列对象
PriorityQueue<TreeNode> queue = new PriorityQueue<TreeNode>(11,
new MyComparator());

// 遍历数组。创建结点对象,放入队列
for (int i = 0; i < counts.length; i++) {
if (counts[i] != 0) {
TreeNode node = new TreeNode(counts[i]);
node.b = (byte) i;// 次数对应的字节
queue.add(node);

}
}

return queue;
}

// 比较器的实现类
private class MyComparator implements Comparator<TreeNode> {

public int compare(TreeNode o1, TreeNode o2) {
return o1.nums - o2.nums;
}
}

/**
* 3.根据优先结点队列构造哈夫曼树
*
* @param queue
*            优先队列
* @return 返回树的根结点
*/
public TreeNode queue2HFMTree(PriorityQueue<TreeNode> queue) {

while (queue.size() > 1) {
// 取出两个权值小的结点
TreeNode node1 = queue.poll();
TreeNode node2 = queue.poll();

// 用两个结点的权值和作为权值创建新结点,作为这个两个结点的父节点
TreeNode root = new TreeNode(node1.nums + node2.nums);
// System.out.println("根节点:"+root.b);
root.left = node1;
root.right = node2;
node1.parent = root;
node2.parent = root;

node1.flag = 0;//左为0
node2.flag = 1;//右为1

// 将新结点加入队列
queue.add(root);//最后一个
}
// 如果队列中只剩下一个结点,即为哈夫曼树的根结点
TreeNode rootNode = queue.poll();
return rootNode;

}

/**
* 根据哈夫曼树得到每个叶子结点的哈夫曼编码
*
* @param root
* @return 将编码放在一个字符串数组中返回,数组长度是256,数组的下标对应每一个字节
*/
public void getHFMCodeByTree(TreeNode root) {
getOneNodeCode(root,"");
}

/**
* 递归得到每个叶结点的编码
*
* @param root 哈夫曼树
* @param codes 存放每个字节编码的字符串数组 数组下标为每个字节
* @param num 生成的哈夫曼编码
*/
public void getOneNodeCode(TreeNode root,String str) {
if (root != null) {
if (root.flag != null) {
str = str + root.flag;
if (root.left == null && root.right == null) {
// 当递归到叶子结点的时候就把得到的字符串放到结点中的字节作为下标的位置
Data d=new Data();//创建一个数据对象
d.bt=root.b;//存入当前字节
d.bm=str;//存入该字节编码
// System.out.println("编码:"+str);
Strcodes.add(d);//将数据对象加入到队列中
}
}
TreeNode left = root.left;
getOneNodeCode(left,str);

TreeNode right = root.right;
getOneNodeCode(right,str);

}

}
/**
* 将字节数组中的01串读取出来,放到一个字符串中
*
* @param codes 存放每个字节编码的字符串数组 数组下标为每个字节
*/
public int[]CutString(ArrayList<Data>Strcodes,String path){
String str="";//创建一个空的字符串
try {
// 建立文件输入流
FileInputStream fis = new FileInputStream(path);
// 包装成数据输入流
DataInputStream dis = new DataInputStream(fis);
// 读取一个字节
while (dis.available()>0) {
byte by = dis.readByte();//读取一个字节
    for(int i=0;i<Strcodes.size();i++){
    if(Strcodes.get(i).bt==by){
    str=str+Strcodes.get(i).bm;
    }
    }
}
// System.out.println("str___________"+str);
// 关闭数据流
fis.close();
} catch (Exception ef) {
ef.printStackTrace();
}
/*
  * 将一整个01字符串8位8位的截取
  * */
int len=(str.length()-1)/8+1;//得到数组的长度
int Intcodes[]=new int[len];//创建一个大小为len的数组
for(int i=0;i<Intcodes.length;i++){
if(str.length()>=8){
String somestr="";//创建一个空字符串
somestr=str.substring(0,;//截取前8位
Intcodes[i]=changeString(somestr);//将前八位转成int型的整数
str=str.substring(8,str.length());//将字符串减小8个字符
// System.out.println("调用了-->"+str);
}else{//如果str的长度小于8
//判断str是否为空
if(str.length()>0){
int size=str.length();
// addzero=(byte)(8-size);//补0数
for(int j=0;j<8-size;j++){//给str补0
    str=str+"0";
// System.out.println("调用了》》"+str);
}
Intcodes[i]=changeString(str);//加入数组
return Intcodes;
}
}
}
return Intcodes;
}
/**
  * 将二进制转换为十进制
  *
  *
  */
public int changeString(String s){
return ((int)s.charAt(0)-48)*128+((int)s.charAt(1)-48)*64+((int)s.charAt(2)-48)*32
+((int)s.charAt(3)-48)*16+((int)s.charAt(4)-48)*8+((int)s.charAt(5)-48)*4
+((int)s.charAt(6)-48)*2+((int)s.charAt(7)-48);
}
//写入压缩文件
public void writeYaFile(int Intcodes[], String npath) {
try {
// 创建文件输出流
java.io.FileOutputStream fos = new java.io.FileOutputStream(npath);
java.io.DataOutputStream dos = new java.io.DataOutputStream(fos);
//先存入存放数据的队列长度
if(Strcodes.size()>0)//判断一下大小
dos.write(Strcodes.size());
// System.out.println("存入原长度:"+Strcodes.size());
//之后依次存入队列中每个数据的字节及其所对应的编码
for(int i=0;i<Strcodes.size();i++){
dos.writeByte(Strcodes.get(i).bt);
// System.out.println("存入字节:"+Strcodes.get(i).bt);
dos.writeByte(Strcodes.get(i).bm.length());//存入编码的长度
// System.out.println("存入编码的长度:"+Strcodes.get(i).bm.length());
dos.writeBytes(Strcodes.get(i).bm);//存入编码
// System.out.println("存入编码:"+ Strcodes.get(i).bm);
}
// //然后存入补0的个数
// dos.writeByte(addzero);
// System.out.println("存入补零数:"+addzero);
//遍历数组,一个字节一个字节存入
//然后存入补0的个数
// dos.writeInt(Intcodes.length);//存一下十进制数组的长度
for (int i = 0; i <Intcodes.length; i++) {
dos.write(Intcodes[i]);
// System.out.println("存入十进制:"+Intcodes[i]);
}
// 强制写出
fos.flush();
fos.close();

} catch (Exception ef) {
ef.printStackTrace();
}

}
/**********************************************************************************************
  * 解压缩
  * ********************************************************************************************/
/*
  * 读取压缩文件
  * npath指压缩后文件的路径
  * epath指解压后文件的路径
  * */
public void JieYa(String npath,String epath){
try {
FileInputStream fis=new FileInputStream(npath);//创建文件输入流
DataInputStream dis=new DataInputStream(fis);//创建数据输入流
//读出数据队列的长度
int length=dis.readByte();
if(length>=-128&&length<=0)
length+=256;//都加上256
// System.out.println("读出数据队列的长度:"+length);
for(int i=0;i<length;i++){//依次读入数据队列中的数据
Data data=new Data();//创建一个数据对象
data.bt=dis.readByte();
byte size=dis.readByte();//读取编码的长度
byte Bt[]=new byte[size];
dis.read(Bt);
data.bm=new String(Bt);//写入编码
Strcodes.add(data);//加入数据队列
// System.out.println("读取字节:"+ data.bt);
// System.out.println("读取编码的长度:"+size);
// System.out.println("读取编码:"+ data.bm);
}
// addzero=dis.readByte();//读取补零数
// System.out.println("读取补零数:"+addzero);
int asize=dis.available();//读取剩余字节数
// System.out.println("读取十进制数组的大小:"+asize);
int codes[]=new int[asize];
for(int i=0;i<asize;i++){//依次读取十进制数
codes[i]=dis.readByte();
if(codes[i]>=-128&&codes[i]<0)
codes[i]+=256;//都加上256
Jiestr+=changeInteger(codes[i]);
// System.out.println("读取十进制数:"+codes[i]);
}
// Jiestr=Jiestr.substring(0, Jiestr.length());//减去补0数
// System.out.println("解压后的字符串:"+Jiestr);
}
catch (Exception ef) {
ef.printStackTrace();
}
try{// 创建文件输出流
java.io.FileOutputStream fos = new java.io.FileOutputStream(epath);
java.io.DataOutputStream dos = new java.io.DataOutputStream(fos);
boolean bool=true;
while(bool){
bool=false;
for(int i=0;i<Strcodes.size();i++){
if(Jiestr.startsWith(Strcodes.get(i).bm)){
dos.writeByte(Strcodes.get(i).bt);//写入文件
Jiestr=Jiestr.substring(Strcodes.get(i).bm.length());//截取字符串
bool=true;
break;
}
}
   }
System.out.println("解压完毕");
} catch (Exception ef) {
// TODO Auto-generated catch block
ef.printStackTrace();
}
}
/*
  * 将十进制转换为二进制
  *
  * */
public String changeInteger(int num){
String str="";//创建一个star用来转换01串
String star="";//创建一个star用来转换01串
while(num!=0){
if(num%2==0){
str=str+"0";
num=num/2;
}
if(num%2==1){
str+="1";
num=num/2;//num除以2
}
}
int size=8-str.length();
for(int i=0;i<size;i++){
str=str+"0";
}
// System.out.println("str长度:"+str.length());
// System.out.println("str:"+str);
str+="0";
for(int i=str.length()-2;i>=0;i--){
star+=str.substring(i,i+1);
}
// System.out.println("star:"+star);
return star;
}
}

分享到:
评论

相关推荐

    hfm.zip_哈弗hfm

    总结来说,这个`hfm.zip_哈弗hfm`压缩包包含了一个实现哈弗曼编码的C++项目,通过分析和操作文件,我们可以了解到如何使用编程语言来实现哈弗曼编码的过程,以及如何测试和验证编码的正确性和效率。这对于理解数据...

    5种压缩算法的JAVA源码:字典算法、串表压缩算法、霍夫曼压缩算法、滑动窗口算法、数据压缩算法

    通过大量的知识资源搜索,总结自己了解的五种压缩算法:LZSS(字典算法)、LZW(串表压缩算法)、Hfm(霍夫曼压算法)、LZ77(滑动窗口算法)、LZAM(数据压缩算法)。 有兴趣的同学自己百度一下各种算法实现原理。 ...

    hfm.rar_doc

    总结来说,哈弗曼编码是一种有效的数据压缩技术,通过考虑字符频率差异生成编码,使得高频字符占用较少的存储空间,从而达到压缩文件的目的。在RAR文档中的"hfm.rar_doc"可能包含了关于如何使用哈弗曼编码进行文件...

    hfm.rar_visual c

    在压缩文件`hfm.cpp`中,包含了以上所述的全部实现逻辑。通过对源代码的分析和调试,我们可以更深入地理解赫夫曼编码的原理和实现细节,同时也能提高编程技能。在实际应用中,这种压缩技术对于大数据量的处理尤其...

    hfm-8-.1.rar_压缩解压_Visual_C++_

    总结来说,这个程序是一个使用Visual C++编写的哈弗曼编码压缩工具,它能对文本文件进行高效压缩,同时计算压缩率,体现了数据压缩理论在实际应用中的价值。通过理解和使用这样的工具,我们不仅可以学习到基本的...

    hfm.rar_哈弗曼 编码 译码

    总结来说,哈弗曼编码和译码是数据压缩领域的重要算法,通过C++实现,可以深入了解数据结构和算法在实际问题中的应用。学习和掌握这一技术对于提升软件开发能力,特别是在处理大数据和优化存储空间方面具有重要意义...

    哈夫曼编码译码器实验报告(免费).docx

    五、总结 哈夫曼编码译码器的实现结合了数据结构和文件操作的知识,通过构建和遍历哈夫曼树,实现了高效的文本压缩与解压缩。在实际应用中,哈夫曼编码常用于数据传输和存储优化,尤其在文本文件压缩领域展现出强大...

    哈夫曼编码译码器实验报告免费.pdf

    3. 通过HFM类实现哈夫曼树的创建和编码译码功能,类中包含根节点指针及各种功能函数,如初始化、输出压缩信息、交换编码顺序、获取根节点和创建哈夫曼树等。 总结来说,哈夫曼编码译码器通过统计字符频率、构建...

    哈弗曼编码课程设计报告

    - **HFM类**: 实现哈弗曼树的创建、编码和解码等功能。 ##### 2. 函数设计 - **init(signode* sig)**: 初始化哈弗曼树节点。 - **exchange()**: 调整哈弗曼编码的顺序,确保正确性。 - **getroot()**: 返回哈弗曼...

    哈夫曼树实验

    总结一下,哈夫曼树是一种为了实现高效编码和数据压缩而设计的数据结构,其核心思想是通过构建最小带权路径长度树,实现字符的前缀编码,从而达到节省存储空间的目的。在理解和应用哈夫曼树时,我们需要掌握哈夫曼...

    GPS数据接收与回放

    总结起来,这个项目可能是一个基于ASP的GPS定位系统,它能够接收GPS数据,存储到服务器,然后通过谷歌地图API在Web客户端上实现轨迹回放功能。开发者需要掌握GPS原理、ASP编程、网络通信、数据库管理和地图API应用等...

Global site tag (gtag.js) - Google Analytics