1. 程式人生 > >Hufman編碼實現運用1 (原理不描述)

Hufman編碼實現運用1 (原理不描述)

思路:

編碼

 

所需標頭檔案:

 

#ifndef HEAD1_H_INCLUDED
#define HEAD1_H_INCLUDED

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<map>
#include<set>
#include<fstream>
using namespace std;
struct HTnode
{
    int parent,lchild,rchild;
    int weight,index;
    HTnode()
    {
        parent=lchild=rchild=-1;
        weight=0;
        index=-1;
    }
};
bool operator <(HTnode b,HTnode a)//對節點結構體進行運算子過載
{
    return b.weight<a.weight;
}
struct _Date
{
    char cc;
    int cnt;
} ;
bool Getbit(char *p,int k)//得到一個位元組從左到右數的第k位
{

    if(*p&(1<<(8-k)))
        return 1;
    return 0;
}
void printbit(char*p)
{
    for(int i=1; i<=8; ++i)
        printf("%d",Getbit(p,i));
}
void Getbitpos(int bitsize,int &sizepos,int &bitpos)//得到第幾個位元組的第幾位
{
    int mid=bitsize/8;
    if(bitsize%8)
    {
        sizepos=mid+1;
        bitpos=bitsize%8;
        return;
    }
    sizepos=mid;
    bitpos=8;
    return;
}
void evalubit(char *p,int k,int flag)//給以一個位元組的從左到右第k位賦0   1
{
    if(flag)
    {
        *p=*p|(1<<(8-k));
        return;
    }
    *p=*p&(~(1<<(8-k)));
}
#endif // HEAD1_H_INCLUDED

 

 

 

編碼程式碼:(因為是雛形 所以這裡採用的是以每一個位元組當作一個字元)

檔名稱說明:

yuandoc.txt     需要壓縮的檔案

date1.txt      壓縮後的檔案

info1.txt       解壓後的檔案

 

 

 

 


/*
原理:
if 前i個字元是週期性的
    then     i-next[i]==T[next[i]]
else    T[i]=i;
*/
/*
Huffman樹
功能:
將TXT文字壓縮
1.計算字元編碼權值
2.構建編碼樹
3.將字串轉化為編碼
4.儲存赫夫曼編碼樹

5.將編碼轉化為字串

程式缺陷 :
只有一種字元編碼時  該字元的編碼為空
*/
#include"head1.h"
class HuffmanTree
{
public:
    int charsize;//字元種類數
    int slen;//讀入的位元組數
    char *read_str;//讀入的字串
    _Date *dateArr;//字元表(字元種類 已經對應出現的次數)
    HTnode *hufArr;//赫夫曼陣列
    char **charCodeArr;//每類字元對應的編碼
    int Allcodesize;
    char *codebitArr;
    HuffmanTree(char *str,int info_size)
    {
        /*
        需要:讀入字元,和位元組數
        結果:輸出字母表dateArr,儲存讀入字元,得到字元種類數charsize,
        */
        hufArr=NULL;
        charCodeArr=NULL;
        codebitArr=NULL;
        slen=info_size;
        read_str=new char[slen];
        for(int i=0;i<slen;++i)
            read_str[i]=str[i];
        map<char,int>mmp;
        map<char,int>::iterator it;
        for(int i=0; i<slen; ++i)
        {
            it=mmp.find(read_str[i]);
            if(it==mmp.end())
                mmp.insert(pair<char,int>(read_str[i],1));
            else
                it->second++;
        }
        charsize=mmp.size();
        dateArr=new  _Date[charsize];
        int top=0;
        for(it=mmp.begin(); it!=mmp.end(); ++it)
        {
            dateArr[top].cc=it->first;
            dateArr[top++].cnt=it->second;
        }
        mmp.clear();
    }
    void BulidTree()
    {
        /*
        需要:字元表,字元個數,
        輸出:哈夫曼陣列,
        */
        hufArr=new HTnode[2*charsize-1];
        multiset<HTnode>hufset;
        for(int i=0; i<charsize; ++i)
        {
            hufArr[i].weight=dateArr[i].cnt;
            hufArr[i].index=i;
            hufset.insert(hufArr[i]);

        }//前n個節點儲存樹節點兵器人加入set容器中
        int top=charsize;
        int lc,rc;
        multiset<HTnode>::iterator it;
        while(top!=2*charsize-1)
        {
            it=hufset.begin();
            lc=it->index;
            hufset.erase(it);
            it=hufset.begin();
            rc=it->index;
            hufset.erase(it);
            hufArr[top].lchild=lc;
            hufArr[top].rchild=rc;
            hufArr[top].weight=hufArr[lc].weight+hufArr[rc].weight;
            hufArr[top].index=top;
            hufArr[lc].parent=top;
            hufArr[rc].parent=top;
            hufset.insert(hufArr[top]);
            top++;
        }
        /*
        二叉樹構建完成
        陣列為hufArr  根節點為2*charsize-2
        對應的字元在dateArr.cc裡面
        */
    }
    void Getcharcode()
    {
        /*
        需要:哈夫曼節點陣列hufArr[],字元表種類數charsize
        輸出:字元對應的編碼 存到charcodeArr[][]
        */
        charCodeArr=new char*[charsize];
        char *midc=new char[charsize+1];//作為中間字串
        int top,now_node,next_node;
        Allcodesize=0;
        for(int i=0; i<charsize; ++i)
        {
            top=0;
            now_node=i;
            while(hufArr[now_node].parent!=-1)
            {
                next_node=hufArr[now_node].parent;
                if(hufArr[next_node].lchild==now_node)
                    midc[top++]='0';
                else
                    midc[top++]='1';
                now_node=next_node;
            }
            midc[top]=0;
            Allcodesize+=top*dateArr[i].cnt;//計算總共需要的位元位數
            charCodeArr[i]=new char[top+1];
            for(int j=0; j<top; ++j) //將第i個字元對應的吧編碼儲存進去
            {
                charCodeArr[i][j]=midc[top-1-j];
            }
            charCodeArr[i][top]='\0';
        }
        delete []midc;
        /*
        得到字元對應的編碼
        */
    }
    void Getbitcode()
    {
        /*
        需要:讀入的字串 read_str ,字元表,每個字元對應的編碼陣列charcodeArr[][],
        輸出:編碼
        */
        int index;//找到字元對應的索引
        int mid;//存這些二進位制數需要的位元組數
        int sizepos,bitpos;
        mid=Allcodesize/8;
        if(Allcodesize%8)
            mid++;
        codebitArr=new char[mid];//申請編碼記憶體
        int top=0;
        for(int i=0; i<slen; ++i)
        {
            for(int j=0; j<charsize; ++j)
                if(dateArr[j].cc==read_str[i])
                {
                    index=j;
                    break;
                }
            int j=0;
            //將此字元對應的編碼儲存到codebitArr陣列中
            while(charCodeArr[index][j])
            {
                ++top;
                Getbitpos(top,sizepos,bitpos);
                if(charCodeArr[index][j]=='0')
                {
                    evalubit(&codebitArr[sizepos-1],bitpos,0);
                }
                else
                {
                    evalubit(&codebitArr[sizepos-1],bitpos,1);
                }
                j++;
            }
        }
        cout<<endl;
        while(top<mid*8)//不夠一個位元組的補上0補夠一個位元組
        {
            ++top;
            Getbitpos(top,sizepos,bitpos);
            evalubit(&codebitArr[sizepos-1],bitpos,0);
        }
        /*
        得到編碼陣列 codebitArr  共Allcodesize 編碼總長度
        */
    }
    void Printcharcode()
    {
        /*
        輸出字元表對應的編碼
        */
    }
    void save_date()
    {
        /*
        初始:字元表種類數 ,bit位的數目, 字母表, Huffman樹 ,編碼
        輸出:儲存
        */
        int mid;
        mid=Allcodesize/8;
        if(Allcodesize%8)
            mid++;
        FILE *fp;
        fp=fopen("../date.txt","wb");
        fwrite(&charsize,4,1,fp);//寫入 字元種類數目
        fwrite(&Allcodesize,4,1,fp);//寫入  bit位數目
        fwrite(&slen,4,1,fp); //寫入原來的位元組數目
        char *chartable;
        chartable=new char[charsize];
        for(int i=0; i<charsize; ++i)
            chartable[i]=dateArr[i].cc;
        fwrite(chartable,1,charsize,fp);//寫入字母表
        delete []chartable;
        fwrite(hufArr,sizeof(HTnode),charsize*2-1,fp);//寫入Huffman 陣列
        fwrite(codebitArr,1,mid,fp);//寫入 編碼
        fclose(fp);
    }
    ~HuffmanTree()
    {
        delete []read_str;
        delete []dateArr;
        if(hufArr)
            delete []hufArr;
        if(codebitArr)
            delete []codebitArr;
        if(charCodeArr)
        {
            for(int i=0; i<charsize; ++i)
                delete []charCodeArr[i];
            delete []charCodeArr;
        }
    }
};
//編碼程式
int main()
{
    char *str;
    int info_size;
    FILE *fp;
    fp=fopen("../yuandoc.txt","rb");//讀取需要壓縮的檔案
    fseek(fp,0L,SEEK_END);
    info_size=ftell(fp);//計算需要壓縮檔案的位元組
    fseek(fp,0L,SEEK_SET);
    str=new char[info_size];
    fread(str,1,info_size,fp);
    HuffmanTree  huftree(str,info_size);
    //讀取字串到str
    huftree.BulidTree();
    huftree.Getcharcode();
    huftree.Getbitcode();
    huftree.save_date();
    fclose(fp);
    delete []str;
//over~~~~~
}

 

 

此程式碼儲存到文字時按照如下方式儲存

 

 

 

 

譯碼就比較簡單了:

 

程式碼:

/*
解壓程式

*/
#include"head1.h"
void Compress_file(FILE *fp)//對檔案進行解壓  當成解壓字元ASSIC碼並存到文本里面(二進位制儲存)
{
    int charsize,Allcodesize,info_size;
    int sizepos,bitpos,now_index;
    char *codetable;
    char *codebitArr;
    char *InfoArr;
    HTnode* hufcodeArr;
    int mid;
    fread(&charsize,4,1,fp);//讀取 charsize
    fread(&Allcodesize,4,1,fp);//讀取  有用的編碼位數
    fread(&info_size,4,1,fp);//讀取 原來的位元組數目
    codetable=new char[charsize];//申請 字母表記憶體
    hufcodeArr=new  HTnode[2*charsize-1];//申請 hufmantree記憶體
    fread(codetable,1,charsize,fp);//讀取  字母表
    fread(hufcodeArr,sizeof(HTnode),charsize*2-1,fp);//讀取哈夫曼樹
    mid=Allcodesize/8;
    if(mid%Allcodesize)
        mid++;
    codebitArr=new char[mid];//申請編碼記憶體
    fread(codebitArr,1,mid,fp);//讀取編碼
    int top=0;
    int cnt=0;
    now_index=2*charsize-2;
    InfoArr=new char[info_size];
    while(top<Allcodesize)
    {
        ++top;
        Getbitpos(top,sizepos,bitpos);
        if(Getbit(&codebitArr[sizepos-1],bitpos))
        {
            now_index=hufcodeArr[now_index].rchild;
        }
        else
        {
            now_index=hufcodeArr[now_index].lchild;
        }

        if(hufcodeArr[now_index].lchild==-1)
        {
            InfoArr[cnt++]=codetable[now_index];
//            printf("now index=%d\n",now_index);
            now_index=2*charsize-2;
//            printf(" cnt=%d, char=%c,ASSIC=%d\n",cnt,InfoArr[cnt-1],(int)InfoArr[cnt-1]);
        }
    }
    FILE *fp2;
    fp2=fopen("../info.txt","wb");
//    printf("infoArr=%s\n",InfoArr);
    fwrite(InfoArr,1,info_size,fp2);
    fclose(fp2);
    delete []codetable;
    delete []hufcodeArr;
    delete []codebitArr;
    delete []InfoArr;
}
int main()
{
    FILE *fp;
    fp=fopen("../date.txt","rb");
    if(fp==NULL)
    {
        printf("error!\n");
        exit(1);
    }

    if(!fp)
        exit(1);
    Compress_file(fp);
    fclose(fp);
}

 

缺陷:

1.只有一種字元無編碼

2.把一個位元組當作一個一種字元   壓縮力度小

後續跟更新4個位元組為以一個字元的編碼程式碼