一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

哈弗曼壓縮實(shí)例(二)壓縮,解壓

 BUPT-BYR 2010-12-15
哈弗曼壓縮實(shí)例(二)壓縮,解壓
 
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include <iomanip>
using namespace std;
struct head{
 unsigned char ch;         //記錄字符
    long count;           //權(quán)值
    int parent,lch,rch;         //定義雙親,左孩子,右孩子
    char bits[256];          //存放哈夫曼編碼的數(shù)組
}header[512],tmp;          //頭部一要定設(shè)置至少512個(gè),因?yàn)榻Y(jié)點(diǎn)最多可達(dá)256,所有結(jié)點(diǎn)數(shù)最多可達(dá)511
unsigned char ctoa(char a[])           /*將數(shù)組的前八位轉(zhuǎn)成二進(jìn)制形式比特位*/
{
    unsigned char c=0;
    for(int i=0;i<8;i++)
    if(a[i]!=0) c=c+(int)(a[i]-'0')*pow(2,8-1-i);
    return c;
}
char *code(unsigned char temp,int leafnum)      //尋找對(duì)應(yīng)字符的編碼串,并返回
{
    for(int i=0;i<leafnum;i++)
       if(temp==header[i].ch)
        return header[i].bits;
    return NULL;
}
void compress(char *infilename,char *outfilename)
{
    long flength=0;                                                 //記錄壓縮前文件長(zhǎng)度
    long clength=8;              //編碼從偏移量8記錄,統(tǒng)計(jì)壓縮后編碼長(zhǎng)度加8
    int leafnum;              //定義葉子結(jié)點(diǎn)
    int pointnum;              //定義總結(jié)點(diǎn)
    unsigned char temp;             //定義unsigned char類型,暫存文件中字符的中間變量
/*********************************文件中字符頻度************************************/
    for(int i=0;i<256;i++)
 {
        header[i].count=0;            //初始化權(quán)重
        header[i].ch=(unsigned char)i;         //初始化字符
 }
    ifstream infile(infilename,ios::in|ios::binary);
    while(infile.peek()!=EOF)
 {
       infile.read((char *)&temp,sizeof(unsigned char));    //讀入一個(gè)字符
       header[temp].count++;          //統(tǒng)計(jì)對(duì)應(yīng)結(jié)點(diǎn)字符權(quán)重
       flength++;              //統(tǒng)計(jì)文件長(zhǎng)度
 }
    infile.close();              //關(guān)閉文件
    for(i=0;i<256-1;i++)            //對(duì)結(jié)點(diǎn)進(jìn)行冒泡排序,權(quán)重大的放在上面,編碼時(shí)效率高
       for(int j=0;j<256-1-i;j++)
       if(header[j].count<header[j+1].count){
           tmp=header[j];
           header[j]=header[j+1];
           header[j+1]=tmp;
    }
    for(i=0;i<256;i++)
       if(header[i].count==0) break;
    leafnum=i;              //取得哈夫曼樹中葉子結(jié)點(diǎn)數(shù)
    pointnum=2*leafnum-1;           //取得哈夫曼樹中總結(jié)點(diǎn)數(shù)目
/**********************************根據(jù)頻度建樹*************************************/
    long min;               //盡量用long,如果文件過大,這里建樹可能不是最優(yōu)樹了
    int s1,s2;
    for(i=leafnum;i<pointnum;i++){
        min=999999999;           
        for(int j=0;j<i;j++)           //挑權(quán)重最小的一個(gè)
          if(header[j].parent==0&&header[j].count<min)
    {
              min=header[j].count;
              s1=j;
    }
        header[s1].parent=i;           //填寫第一個(gè)葉子結(jié)點(diǎn)信息
        min=999999999;
        for(j=0;j<i;j++)            //挑權(quán)重最小的第二個(gè)
            if(header[j].parent==0&&header[j].count<min)
   {
                min=header[j].count;
                s2=j;
   }
        header[s2].parent=i;
        header[i].count=header[s1].count+header[s2].count;    //填寫父結(jié)點(diǎn)信息
        header[i].lch=s1;
        header[i].rch=s2;
 }
/*********************************根據(jù)哈夫曼樹編碼**********************************/
    char tmp[256];              //定義臨時(shí)變量,暫存編碼
    tmp[255]='\0';              //添加結(jié)束標(biāo)志
    int start;
    int c;                //記錄當(dāng)前葉結(jié)點(diǎn)下標(biāo)
    int f;                //存儲(chǔ)父結(jié)點(diǎn)的下標(biāo)
    for(i=0;i<leafnum;i++){
        start=255;              //另開始等于數(shù)組最后位
        for(c=i,f=header[i].parent;f!=0;c=f,f=header[f].parent)   //對(duì)葉結(jié)點(diǎn)進(jìn)行編碼
            if(header[f].lch==c) tmp[--start]='0';
            else tmp[--start]='1';
        strcpy(header[i].bits,&tmp[start]);
 }
/************************************對(duì)文件進(jìn)行編碼,寫入新文件(核心)*********************************/
    infile.open(infilename,ios::in|ios::binary);       //打開待壓縮的文件
    infile.clear();
    infile.seekg(0);
    ofstream outfile(outfilename,ios::out|ios::binary);      //打開壓縮后將生成的文件
    outfile.write((char *)&flength,sizeof(long));       //寫入原文件長(zhǎng)度
    char buf[513]="\0"; //初始化編碼緩沖區(qū)
    outfile.seekp(8);              //指針定向偏移量8
    while(infile.peek()!=EOF){
        infile.read((char *)&temp,sizeof(unsigned char));     //讀入字符
        strcat(buf,code(temp,leafnum));          //檢索出字符對(duì)應(yīng)編碼,連到buf[]中
        while(strlen(buf)>=8)            //當(dāng)buf中字符長(zhǎng)度大于8時(shí),一直處理寫入,直至小于8
  {
            temp=ctoa(buf);             //上面臨時(shí)變量已經(jīng)完成使命,可以賦新值了
            outfile.write((char *)&temp,sizeof(unsigned char));    //轉(zhuǎn)成二進(jìn)制寫入
            clength++;              //統(tǒng)計(jì)代碼結(jié)尾偏移加1,用于找到葉子結(jié)點(diǎn)位置
            strcpy(buf,buf+8);                                          //字符串前移八位
  }    //當(dāng)此循環(huán)結(jié)束時(shí),表示buf[]中已經(jīng)小于8了,沒到文件末尾,讀下一個(gè),繼續(xù),否則退出
 }   //while 此層循環(huán)退出時(shí),表示已到末尾,再判斷buf中是否寫完,沒寫完,連滿至少8個(gè)字符,再寫一個(gè)字節(jié),就夠了
    if(strlen(buf)>0){
       strcat(buf,"0000000");
       temp=ctoa(buf);              //前八位轉(zhuǎn)成二進(jìn)制形式
       outfile.write((char *)&temp,sizeof(unsigned char));
       clength++;               //統(tǒng)計(jì)代碼結(jié)尾偏移加1,用于找到葉子結(jié)點(diǎn)位置
 }
    outfile.seekp(4);
    outfile.write((char *)&clength,sizeof(long));       //寫入文件中將記錄葉子結(jié)點(diǎn)位置
    infile.close();
/*************************************將字符編碼對(duì)照表寫入文件****************************************/
    long bytelen;                //記錄編碼以二進(jìn)制存儲(chǔ)時(shí)需要占多少個(gè)字節(jié)
    outfile.clear();
    outfile.seekp(clength);             //將文件指針移到編碼后面的第一位置,在此處記錄葉子結(jié)點(diǎn)數(shù)
    outfile.write((char *)&leafnum,sizeof(long));       //寫入葉子結(jié)點(diǎn)數(shù)
    for(i=0;i<leafnum;i++)
 {
        outfile.write((char *)&header[i].ch,sizeof(unsigned char));   //寫入字符
        header[i].count=strlen(header[i].bits);        //不再設(shè)置其他變量,權(quán)值這時(shí)已無使用價(jià)值,可以用相應(yīng)結(jié)點(diǎn)的權(quán)值變量記錄長(zhǎng)度
        outfile.write((char *)&header[i].count,sizeof(unsigned char)); //寫入長(zhǎng)度的ASCII碼
        if(header[i].count%8==0) bytelen=header[i].count/8;
        else {
            bytelen=header[i].count/8+1;
            strcat(header[i].bits,"0000000");        //在編碼后面補(bǔ)0,使其最后湊滿8的倍數(shù),
                   //超過無妨,可以用bytelen控制好寫入字節(jié)的長(zhǎng)度
  }
        for(int j=0;j<bytelen;j++){
            temp=ctoa(header[i].bits);
            outfile.write((char *)&temp,sizeof(unsigned char));
            strcpy(header[i].bits,header[i].bits+8);          
  }
 }   //此循環(huán)結(jié)束后就完成了編碼對(duì)照表的寫入
}//compress
void ctoa(unsigned char a,char code[])        /*字符轉(zhuǎn)為二進(jìn)制形式存入8位數(shù)組*/

    int n=9;
    for(int i=0;i<n;i++) code[i]='0';     //整個(gè)字符串初始化
    code[n-1]='\0';          //添加結(jié)束標(biāo)志
    n=n-2;
    int c=(int)a;
    while(c>0){
       code[n--]=c%2+'0';
       c=c/2;
 }
}
int strcmp1(char buf[],struct head head[],int n,unsigned char &c) //將buf字符串與header[i].bits[]中匹配,成功后對(duì)應(yīng)的字符由c帶回
{
    for(int i=0;i<n;i++)
        if(strcmp(buf,head[i].bits)==0){
            c=head[i].ch;
            return 1;
  }
    return 0;
}
void strcpy1(char buf[],char a[],int j)   //將字符串a(chǎn)中長(zhǎng)度為j的部分復(fù)制到buf數(shù)組中
{
    for(int i=0;i<j;i++)
    buf[i]=a[i];
    buf[i]='\0';
}

void uncompress(char *infilename,char *outfilename)
{
    long flength;          //定義原文件長(zhǎng)度,從壓縮后文件前四字節(jié)獲取值
    long clength;          //獲取編碼長(zhǎng)度后的第一偏移量,從壓縮文件第五字節(jié)開始獲取值
    int n;            //葉子結(jié)點(diǎn)數(shù),從編碼尾端開始獲取
    string str;           //讀取編碼到字符串,好進(jìn)行統(tǒng)一解碼
    char code[9];          //將字符轉(zhuǎn)為二進(jìn)制數(shù)組形式暫存
    unsigned char temp;         //讀取字符存放此臨時(shí)變量
    long readlen=0;          //記錄已經(jīng)讀取的長(zhǎng)度(讀文件解碼時(shí)用)
    long writelen=0;         //記錄已經(jīng)寫入的字節(jié)
    long clen;           //臨時(shí)變量,讀取字符編碼對(duì)照表時(shí)使用
/************************************讀入必要的數(shù)據(jù)*****************************************************/
    void ctoa(unsigned char a,char code[]);    //需要調(diào)用的函數(shù)的聲明
    ifstream infile(infilename,ios::binary);
    if(!infile) {
        cerr<<"error"<<endl;
        return;
 }
    infile.read((char *)&flength,sizeof(long));   //讀入原始文件長(zhǎng)度,用于解碼時(shí)判斷
    infile.read((char *)&clength,sizeof(long));   //讀入葉子結(jié)點(diǎn)起始位置
    infile.seekg(clength);
    infile.read((char *)&n,sizeof(int));    //讀入葉子結(jié)點(diǎn)數(shù)
/************************************讀入編碼對(duì)照表,放入header[i].bits[]數(shù)組中**************************/
    infile.seekg(clength+4);             //文件指針定位到字符編碼對(duì)照表的起始
    for(int i=0;i<n;i++)              //逐個(gè)讀入葉子結(jié)點(diǎn)數(shù),將編碼進(jìn)行讀入
 {
        infile.read((char *)&header[i].ch,sizeof(unsigned char));    //讀入字符
        infile.read((char *)&header[i].count,sizeof(unsigned char));   //讀入編碼長(zhǎng)度
        clen=(int)header[i].count;
        int diff=clen%8;
        if(0==clen%8)               //計(jì)算需要讀取多少個(gè)字節(jié)
            clen=clen/8;
        else clen=clen/8+1;
        header[i].bits[0]='\0';             //初始化,方便后面進(jìn)行連接
        for(int j=0;j<clen;j++)             //連接編碼,使之存入header[i].bits[]中
  {
            infile.read((char *)&temp,1);
            ctoa(temp,code);
            strcat(header[i].bits,code);             //將轉(zhuǎn)換過來的編碼進(jìn)行連接
  }
        int bitlen=strlen(header[i].bits);
        if(0!=diff)
        header[i].bits[bitlen-8+diff]='\0';
 }//for(int i=0;i<n;i++)
/************************************對(duì)讀入的編碼對(duì)照表進(jìn)行排序,長(zhǎng)度短的排在前面***********************/
    for(i=0;i<n;i++){
        for(int j=0;j<n-i-1;j++){
            if(header[j].count>header[j+1].count){
                tmp=header[j];
                header[j]=header[j+1];
                header[j+1]=tmp;
   }
  }
 }
/************************************將編碼讀入內(nèi)容,進(jìn)行解碼工作************************************/
    readlen=0;
    writelen=0;
    ofstream outfile(outfilename,ios::binary|ios::out);     //打開編碼后文件
    if(!outfile)
 {
        cerr<<"輸出文件打開失敗"<<endl;
        return;
 }
    char buf[513]="\0";              //讀入編碼緩沖區(qū)
    char buf1[257]="\0";
    infile.seekg(8);             /* 讀取編碼,解壓連入緩沖區(qū) */
    while(1)       
 {
        while(readlen<(clength-8)&&strlen(buf)<=256)      //讀滿緩沖區(qū)
  {
            infile.read((char *)&temp,sizeof(temp));
            ctoa(temp,code);           //將字節(jié)轉(zhuǎn)為數(shù)組
            strcat(buf,code);  
            readlen++;
  }//while
        while(strlen(buf)>=256)           //處理緩沖區(qū),直到少于256位,再讀滿它
  {
            for(i=0;i<strlen(buf);i++)
   {
                strcpy1(buf1,buf,i+1);         //逐漸增多取,放入buf1,進(jìn)行匹配
                if(strcmp1(buf1,header,n,temp)==1)
    {
                    outfile.write((char *)&temp,sizeof(unsigned char));
                    writelen++;
                    strcpy(buf,buf+i+1);                            //緩沖區(qū)前移
                    break;
    }
   }//for
            if(writelen>=flength) break;         //如果寫入達(dá)到原文件長(zhǎng)度,退出
  }//while
        if(readlen>=(clength-8)/*編碼長(zhǎng)度*/||writelen>=flength) break;   //如果寫入或者讀入編碼完畢,退出
 }//退出此循環(huán)后,還有未解碼完成的buf[]
//對(duì)buf[]緩沖的善后處理
    while(writelen<flength)
 {
        for(i=0;i<strlen(buf);i++)
  {
            strcpy1(buf1,buf,i+1);
            if(strcmp1(buf1,header,n,temp)==1)
   {
                outfile.write((char *)&temp,sizeof(unsigned char));
                writelen++;
                strcpy(buf,buf+i+1);
                break;
   }
  }//for
 }
    infile.close();      //關(guān)閉文件
    outfile.close();
}//uncompress()
int main(int num,char *cmdline[])
{
    char infilename[256],outfilename[256],select[255];
    if(num>3)
 {
       strcpy(select,cmdline[1]);
       strcpy(infilename,cmdline[2]);
       strcpy(outfilename,cmdline[3]);
 }
    else if(num==1)
 {
       cout<<"輸入命令[壓縮yasuo/解壓jieya](例如:yasuo):";cin>>select;
       cout<<"[源文件](例如d:\\RedMansionDream.txt):";cin>>infilename;
       cout<<"[目標(biāo)文件](例如:d:\\t1.txt):";cin>>outfilename;
 }
    if(!strcmp(select,"yasuo"))
        compress(infilename,outfilename);
    else if(!strcmp(select,"jieya"))
        uncompress(infilename,outfilename);
    return 0;
 
}

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    中文字幕无线码一区欧美| 青青操视频在线播放免费| 日本欧美一区二区三区在线播| 亚洲国产欧美精品久久| 日韩在线中文字幕不卡| 不卡免费成人日韩精品| 亚洲精品国产主播一区| 日韩成人高清免费在线| 亚洲男人的天堂色偷偷| 欧美熟妇喷浆一区二区| 久久精品国产99精品最新| 亚洲综合一区二区三区在线| 欧美日韩国产黑人一区| 国产超薄黑色肉色丝袜| 日本成人中文字幕一区| 青青操视频在线观看国产| 黄色片国产一区二区三区| 日本二区三区在线播放| 在线欧美精品二区三区| 色婷婷中文字幕在线视频| 区一区二区三中文字幕| 性感少妇无套内射在线视频| 成人午夜激情免费在线| 91香蕉视频精品在线看| 国产一区二区三区精品免费| 亚洲人午夜精品射精日韩| 精品女同一区二区三区| 日本不卡片一区二区三区| 日韩欧美好看的剧情片免费| 男女午夜福利院在线观看| 亚洲欧美日韩在线中文字幕| 精品久久少妇激情视频| 九九视频通过这里有精品| 91免费精品国自产拍偷拍| 成人精品网一区二区三区| 国产又粗又黄又爽又硬的| 欧美日韩视频中文字幕| 欧美黑人在线一区二区| 国产剧情欧美日韩中文在线| 亚洲国产av一二三区| 九九热视频免费在线视频|