HUFFMAN编码进行压缩---【数据结构】

说到HUFFMAN编码可以用来进行压缩,然后就想试试。拖了一周才开始写。纠结了两天。现在有点样子了,但是。。有BUG。。。有的时候可以有的时候不可以。。。。正在找原因。对文件操作和计算机原理这一块还不是很熟。


理一下思路吧。

先从一个文件中读取数据,进行频数统计,然后根据统计的频数建立HUFFMAN树。再扫描一遍文件,利用HUFFMAN树进行编码。此处有技巧!因为要进行压缩,所以对于每个字符的编码应用位来储存。这时候就不能想着用字符串来表达位啦!取一个字符c。我们知道一个字符有8位(一个字节),然后把c当做一个字节来看,而不是一个字符。对它进行位的存储。集满8位编码就把它存到输出文件中。

结束后从输出文件中读入字符,进行解码,把结果放到另一个文件中,和读入文件进行比对。

这里有偷懒,编码的时候应该把HUSH情况(字符对应的编码)一起放进去。这样如果程序只是执行解码操作时也是可以执行的。

建立HUFFMAN树可以用优先队列进行,这样比较方便,效率也高一些。但是要注意,如果没有对优先队列的算子?进行重定义,若放的是节点指针是不可取的!!这里再次验证了指针真的只是一个int!此处有坑。

然后是unsigned char 和 char。之前只知道有unsigned int 和 int,并且从来认为unsigned int是一个多余的类型。也从来认为补码只在题目中出现。这次俩碰一块了。因为只有8位,如果直接定义char,那么范围是-128~127,可是字符有256个,所以要用unsigned char来接收,这样就没问题啦。计算机存数据的时候都是用补码来存放的,并不区分类型,只有当你使用的时候类型才有了意义。


HUFFMAN编码是无所压缩最优的办法,但是当文件过小就可能出现压缩不利的情况,或者文件过大使得每个字符的出现频率相近,压缩率也很小。


看看我千疮百孔的代码。仍需修改但是在头疼所以mark一下以兹鼓励。其中有些是用来debug的。。。

***********************************************************************

终于知道哪里错了!!!!!就说哪里好奇怪的呢。。

这么大的一个bug为什么我会忽视掉??

在编码的时候,如果编到最后一位,未满一个字节是往后补零的。但是!这很有可能正好碰到一个huffman编码是这若干个零的!所以!在读入文件的时候要计算字符的个数,在解码的时候如果到达这个个数了,就要跳出循环!

我个傻叉QAQ。然后在读入文件的时候我省事了,没有输入路径(一不小心手抖了就错了这样测试起来很麻烦的说)。

顺便让我把代码改的优雅一点。

#include <stdio.h>
#include <string.h>
#include <queue>
#include <string>
#include <iostream>
using namespace std;
class node
{
public:
    int l,r,sum,pos;
    char c;
    string wod;
    
    node(){}
    node(int l,int r,int sum,int pos,char c,string wod):l(l),r(r),sum(sum),c(c),wod(wod),pos(pos){}
    
    void init(int l,int r,int sum,int pos,char c,string wod)
    {this->l=l,this->r=r,this->sum=sum,this->c=c,this->wod=wod;this->pos=pos;}
    
    friend bool operator < (const node a,const node b){return a.sum>b.sum;}
}tree[512];
int hashd[256],rehash[256];
void build(int p,string s)
{
    if(p==-1) return;
    if(tree[p].l==-1&&tree[p].r==-1)
    {
        tree[p].wod=s;
        return;
    }
    build(tree[p].l,s+"0");
    build(tree[p].r,s+"1");
}
int search(int root,int p)
{
    if(p) return tree[root].r;
    else return tree[root].l;
}
int move(int a,int b)
{
    while(b--) a<<=1;
    return a;
}
char s[8];
char* change(int x)
{
    char tmp[8];
    for(int i=0;i<8;i++) {tmp[i]=x&1;x>>=1;}
    for(int i=0;i<8;i++) s[i]=tmp[7-i];
    return s;
}
int main()
{
    //文件预操作
    FILE *ifp,*ofp;
    char infile[257],outfile[257];
    cout<<"输入文件的路径:"<<endl;
    cin>>infile;

    ifp=fopen(infile,"rb");
    if(ifp==NULL) {cout<<"Fail to open"<<endl;return 0;}
    cout<<"输出文件的路径:"<<endl;
    cin>>outfile;

    ofp=fopen(outfile,"wb");
    if(ofp==NULL) {cout<<"Fail to write"<<endl;return 0;}
     
    
    int c;int len=0;
    memset(hashd,0,sizeof(hashd));

    //建立huffman树,获得编码
           while((c=fgetc(ifp))!=EOF)
    {
        hashd[c]++;
        len++;
    }
       create huffman
    int top=0;
    priority_queue<node>que;
    for(int i=0;i<256;i++)
    {
        if(!hashd[i]) continue;
        tree[top].init(-1,-1,hashd[i],top,i,"");
        rehash[i]=top;
        que.push(tree[top++]);
    }
    while(que.size()>1)
    {
        node p=que.top();que.pop();
        node h=que.top();que.pop();
        tree[top].init(p.pos,h.pos,p.sum+h.sum,top,-1,"");
        que.push(tree[top++]);
    }
    node root=que.top();que.pop();
    build(root.pos,"");
    
    
    fseek(ifp,0,SEEK_SET);
    
    char to=0;
    int size=0;
    int j=0;
    //进行编码
    while((c=fgetc(ifp))!=EOF)
    {
        int num=rehash[c];
        string tmp=tree[num].wod;
                int i=0;
        while(i<tmp.length())
        {
            for(j=size;j<8&&i<tmp.length();j++,i++)
            {
                to<<=1;
                if(tmp[i]=='1') to|=1;
            }
            size=j%8;
            if(size==0) {fwrite(&to,1,1,ofp);to=0;}
        }
    }
    if(size!=0)
    {
        for(j=size;j<8;j++) to<<=1;
        fwrite(&to,1,1,ofp);
    }
    fclose(ifp);
    fclose(ofp);
    
    //译码
    ifp=fopen(infile,"wb");
    ofp=fopen(outfile,"rb");
    int st=root.pos;
    string buf="";
    while((c=fgetc(ofp))!=EOF)
    {
        buf+=char(c);
    }
    int lent=0;
    for(int i=0;i<buf.length();i++)
    {
        for(int j=0;j<8;j++)
        {
            int k=move(1,7-j);
            st=search(st,buf[i]&k);
            if(tree[st].l==-1&&tree[st].r==-1)
            {fwrite(&tree[st].c,1,1,ifp);lent++;st=root.pos;}
            if(lent>=len) break;
        }
        if(lent>=len) break;
    }
    fclose(ifp);
    fclose(ofp);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值