系统:win10

工具:vc6.0

//我加了个计时,用int存储字符总数

//因此增加一个限制:文件不可大于2GB

#include<iostream>
#include<time.h>

#define ASCIIL 256
#define F(x) ((x-1)>>1)
#define L(x) ((x<<1)+1)
#define R(x) ((x<<1)+2)
#define SWAP(a,b,tmp) {tmp=a;a=b;b=tmp;}

using namespace std;

int soufail_charcount;

//实现二叉堆模板类,小顶堆
template <class HeapType>
class CHeap
{
    HeapType *data,tmp;
    int size;
    
    void HeapUp(int ix)
    {//自底向顶维护堆
        int f;
        for(f=F(ix);ix&&data[ix]<data[f];ix=f,f=F(f))
            SWAP(data[ix],data[f],tmp);
    }
    
    void HeapDown(int ix)
    {//自顶向底维护堆
        int l,r,t;
        HeapType min,tmp;
        if(ix>=size) return ;
        l=L(ix),r=R(ix);
        
        min=data[ix],t=ix;
        
        if(l<size&&data[l]<min)
            t=l,min=data[l];
        if(r<size&&data[r]<min)
            t=r,min=data[l];
        
        SWAP(data[ix],data[t],tmp);
        if(ix!=t) HeapDown(t);
    }
    
public:
    CHeap()
    {//这里特殊应用,堆内元素个数不超过256
        size=0;
        data=new HeapType[256];
    }
    ~CHeap()
    {//释放内存
        delete [] data;

}
    int getsize()
    {//返回堆大小
        return size;
    }
    void clear()
    {//清空堆
        size=0;
    }
    void insert(HeapType e)
    {//向堆尾中插入元素,并向顶维护堆
        data[size++]=e;
        HeapUp(size-1);
    }
    HeapType top()
    {//从堆顶取出元素,并向底维护堆
        HeapType ret=data[0];
        data[0]=data[--size];
        HeapDown(0);return ret;
    }
};

//哈夫曼树结点结构体实现
typedef struct talNode{
    
    unsigned char c;    //叶结点时对应ASCII值
    int weight;            //该结点权值
    int lt,rt;            //左、右结点下标
    
    talNode(){}
    
    talNode(unsigned char _c,int _p):
    c(_c),weight(_p),lt(-1),rt(-1)
    {}
    
    talNode(unsigned char _c,int _p,int l,int r)
        :c(_c),weight(_p),lt(l),rt(r)
    {}
    
    bool operator < (talNode a)
    {//重载运算符"<"用于二叉堆内的比较
        return weight<a.weight;
    }
    
}HuffNode;

//哈夫曼文件压缩类声明

class CHuffMan{
    HuffNode arr[512];        //哈夫曼树结点数组
    int size;                //哈夫曼树结点个数
    bool code[256][64];        //ASCII对应编码方案
    int lenth[256];            //ASCII对应编码长度

//lastcodelenth,ps[256],用于存储压缩文件中作为文件头
    int lastcodelenth;        //文件最后一个字符实用几位
    int ps[256];            //ASCII对应出现频率
    int soucelen,targetlen; //源及目标文件长度

//私有成员函数,用于实现内部功能
    void SetHuffTree(int []);            //根据字符频率生成哈夫曼树
    void SetHuffCode(int ,bool [],int );//根据哈夫曼树生成编码方案
    void CreateHuff(int []);            //创建哈夫曼树及编码方案
    void EnCodePre(char []);            //压缩前预处理
    void DeCodePre(FILE *);                //解压前预处理
    void SaveHuffHead(FILE *);            //保存压缩文件头
    void ReadHuffHead(FILE *);            //读取压缩文件头

public:
    CHuffMan(){Clear();}                //构造函数
    ~CHuffMan(){}                        //析构函数
    
    //公有成员函数,用于提供使用接口
    void Clear();                        //清空当前对象内容
    void EnCodeFile(char [],char []);    //编码,用于压缩文件,第一个参数为
                                        //源文件名,第二个参数为目标文件名
    void DeCodeFile(char [],char []);    //解码,用于解压文件,第一个参数为
                                        //源文件名,第二个参数为目标文件名
    void GetHuffCode();                    //输出ASCII对应编码方案
    int GetSouceLen();                    //输出当前工作项的源文件长度
    int GetTargetLen();                    //输出当前工作项的目标文件长度
    
};

void CHuffMan::SetHuffTree(int ps[])
{                                        //每次取出两权值最小的结点合并成新树,
                                        //加入堆,直至堆中只余有一个元素

CHeap<HuffNode> hp;                    //二叉堆对象
    
    for(int i=0;i<ASCIIL;i++){            //如果字符i出现过,则插入二插堆
        if(ps[i])
            hp.insert(HuffNode(i,ps[i]));
    }
    size=0;                                //初始化哈夫曼树中结点个数
    while(hp.getsize()>1){
        arr[size++]=hp.top();            //取出权值最小的两个结点
        arr[size++]=hp.top();
        hp.insert(HuffNode(0,
            arr[size-1].weight+arr[size-2].weight,
            size-1,size-2));            //合并结点,并插入堆
    }
    arr[size++]=hp.top();                //arr[size-1]为哈夫曼树根
}

void CHuffMan::SetHuffCode(int ix,bool stk[],int top)
{                                        //递归深搜哈夫曼树,生成所有存在的ASCII
                                        //的前缀码
    if(arr[ix].c){                        //如果
        if(top){                        //此判断用于只含有一类字符的文件
            memmove(code[arr[ix].c],stk,sizeof(bool)*top);
            lenth[arr[ix].c]=top;
        }
        else lenth[arr[ix].c]=1;
        return ;
    }
    stk[top]=0;                            //左子树的边设为0
    SetHuffCode(arr[ix].lt,stk,top+1);    //递归进入左子树
    stk[top]=1;                            //右子树的边设为1
    SetHuffCode(arr[ix].rt,stk,top+1);    //递归进入右子树
}

void CHuffMan::CreateHuff(int ps[])
{                                        //构造哈夫曼树及前缀码
    bool stk[64];
    SetHuffTree(ps);                    //根据字符频率构造哈夫曼树
    SetHuffCode(size-1,stk,0);            //根据哈夫曼树生成前缀码
}
void CHuffMan::EnCodePre(char sfilename[])
{                                        //压缩文件预处理,读取字符出现频率
    FILE *fp;                            //及构造哈夫曼树及前缀码
    int c;
    fp=fopen(sfilename,"rb");
    if(fp==NULL){ 
        cout<<"读取文件错误"<<endl;
        exit(0);
    }
    memset(ps,0,sizeof(ps));            //读取字符出现频率
    while(true){
        c=fgetc(fp);
        if(feof(fp))break;
        ps[c]++;
    }
    fclose(fp);
    CreateHuff(ps);                        //构造哈夫曼树及前缀码
}

void CHuffMan::DeCodePre(FILE *fp)
{                                        //解压文件预处理,读取压缩文件头
                                        //根据读取头信息构千哈夫曼树及前缀码
    ReadHuffHead(fp);
    CreateHuff(ps);
}

void CHuffMan::SaveHuffHead(FILE *fp)
{                                            //向压缩文件中写文件头
    fwrite((void *)&lastcodelenth,4,257,fp);//从lastcodelenth的地址开始的连续
                                            //4*257个字节,即lastcodelenth和
                                            //ps[256]数组内容
    targetlen+=4*257;
}

void CHuffMan::ReadHuffHead(FILE *fp)
{                                        //从缩文件中读文件头
    fread((void *)&lastcodelenth,4,257,fp);    //从lastcodelenth的地址开始的连续
                                            //4*257个字节,即lastcodelenth和
    soucelen+=4*257;                        //ps[256]数组内容
}

void CHuffMan::Clear()
{                                            //清空前前工作项
    size=0;soucelen=targetlen=0;
    lastcodelenth=0;
    memset(lenth,0,sizeof(lenth));
    memset(ps,0,sizeof(ps));
}

int CHuffMan::GetSouceLen()
{                                            //获取当前工作项的源文件长度
    return soucelen;
}

int CHuffMan::GetTargetLen()
{                                            //获取当前工作项的目标文件长度
    return targetlen;
}

void CHuffMan::GetHuffCode()
{                                            //输出当前工作项的编码前缀码方案
    int i;
    for(i=0;i<ASCIIL;i++){
        if(lenth[i]>0){                        //如果前缀码不空
            printf("%c : ",i);                //输出ASCII码
            for(int j=0;j<lenth[i];j++){
                printf("%d",code[i][j]);    //输出对应前缀码
            }
            puts("");
        }
    }
}

void CHuffMan::EnCodeFile(char sfilename[],char gfilename[])
{
                                            //将文件sfilename
                                            //压缩为文件gfilename[]
    FILE *fp,*fpt;
    int c,data,l,i;
    
    EnCodePre(sfilename);                    //压缩预处理,生成哈曼树及
                                            //字符前缀码
    
    fp=fopen(sfilename,"rb");
    fpt=fopen(gfilename,"wb");
    
    SaveHuffHead(fpt);                        //写入压缩文件的头信息
                                            //!!!注意,此时lastcodelenth
                                            //为空,需压缩结束时重置
    l=data=0;
    puts("Encoding ... ");

clock_t start = clock();
    clock_t finish;
    double consumeTime;
    int i1=0;

//编码压缩过程,依次对源文件字符进行编码
    while(true){                            //存入一个字符中,用移位操作实现,每8位前
        c=fgetc(fp);                        //缀码对应一个字符,将该字符存入目标文件,
        if(feof(fp)) break;                    //最终不足8位的记录最后一位占用的前缀码长度
        soucelen++;                            //源文件长度增加
        for(i=0;i<lenth[c];i++){            //对data进行左移,空出最低位
            data<<=1;                        //对当前字符的前缀码当前们存储于data中
            data+=code[c][i];    
            if(++l%8==0){                    //满8位,则存储
                fputc(data,fpt);
                targetlen++;                //目标文件长度增加
            }
        }

i1++;
    if(i1==soufail_charcount/4)
    {
        finish = clock();
        consumeTime = (double)(finish-start)/CLOCKS_PER_SEC;

cout<<endl<<endl<<"已压缩:25%"<<"     "<<"用时:"<<consumeTime<<" 秒"<<endl;

}
    if(i1==soufail_charcount/2)
    {
        finish = clock();
        consumeTime = (double)(finish-start)/CLOCKS_PER_SEC;

cout<<endl<<endl<<"已压缩:50%"<<"     "<<"用时:"<<consumeTime<<" 秒"<<endl;

}
    if(i1==soufail_charcount*3/4)
    {
        finish = clock();
        consumeTime = (double)(finish-start)/CLOCKS_PER_SEC;

cout<<endl<<endl<<"已压缩:75%"<<"     "<<"用时:"<<consumeTime<<" 秒"<<endl;

}

}

finish = clock();
    consumeTime = (double)(finish-start)/CLOCKS_PER_SEC;

cout<<endl<<endl<<"已压缩:100%"<<"     "<<"用时:"<<consumeTime<<" 秒"<<endl;
    cout<<endl<<endl;

//对最后的一个字符进行处理
    lastcodelenth=l%8;                        //记录实际占用位的长度
    data<<=8-lastcodelenth;                    //空出剩余位
    fputc(data,fpt);                        //输出至文件
    targetlen++;                            //目标文件长度增加
    
    fseek(fpt,0,SEEK_SET);                    //!!!回溯至文件头,更新lastcodelenth至
    fwrite(&lastcodelenth,4,1,fpt);            //真实值
    
    fclose(fp);                                //关闭文件
    fclose(fpt);
}

void CHuffMan::DeCodeFile(char sfilename[],char gfilename[])
{                    
    
    clock_t start = clock();
    clock_t finish;
    double consumeTime;
    
                                            //解压文件sfile至gfile

FILE *fp=fopen(sfilename,"rb");
    FILE *fpt=fopen(gfilename,"wb");
    int c,t,l,i;                            //l用于记录当前前缀码段的长度
    HuffNode cur;
    bool tmp[64];                            //tmp[]用于记录当前的前缀码段
    
    DeCodePre(fp);
    l=0;                                    
    puts("Decoding ... ");
    
    fscanf(fp,"%c",&c);                        //解码过程,压缩过程的逆过程,取出编码了的字符,
                                            //按位取出,存于tmp[]中,找出前缀码对应的字符
    while(!feof(fp)){
        soucelen++;
        fscanf(fp,"%c",&t);
        if(feof(fp))break;
        
        for(i=l+7;i>=l;i--){                //按位取出前缀码
            tmp[i]=c&1;c>>=1;
        }l+=8;
        
        while(l>=32){                        //如果当前前缀码段超出一定的长度,则取出前缀码
                                            //进行解码
            for(i=0,cur=arr[size-1];!cur.c;i++)
                cur=tmp[i]?arr[cur.rt]:arr[cur.lt];//找到前缀码段对应第一个字符
            fprintf(fpt,"%c",cur.c);                //输出至目标文件        
            l-=i;targetlen++;                        //前缀码段减去当前字符前缀码长度
            memmove(tmp,tmp+i,sizeof(bool)*l);        //数组顺移至开头,即从0开始记录当前的
                                                    //前缀码段
        }c=t;
    }
    for(i=l+7;i>=l;i--){                            //对最后一个字符做特殊处理
        tmp[i]=c&1;                                    //取出每一位
        c>>=1;
    }
    l+=lastcodelenth;                                //只利用最后一个字符的前lastcodelenth位
    while(l){                                        //输出剩余的前缀码段对应的字符
        for(i=0,cur=arr[size-1];!cur.c;i++)
            cur=tmp[i]?arr[cur.rt]:arr[cur.lt];
        fprintf(fpt,"%c",cur.c);l-=i;targetlen++;
        memmove(tmp,tmp+i,sizeof(bool)*l);
    }
    fclose(fp);fclose(fpt);                            //关闭文件

finish = clock();
    consumeTime = (double)(finish-start)/CLOCKS_PER_SEC;

cout<<endl<<endl<<"解压用时:"<<consumeTime<<" 秒"<<endl;
    cout<<endl<<endl;

}

bool Menu(int &op)
{
    system("cls");
    printf("|\t哈夫曼编码实现文件压缩\t|\n");
    printf("功能:\n");
    printf("_________________________________\n");
    printf("|\t1、\t压缩文件\t|\n");
    printf("|\t2、\t解压文件\t|\n");
    printf("|\t3、\t输出编码方案\t|\n");
    printf("|\t0、\t退出     \t|\n");
    printf("---------------------------------\n");
    do{
        printf("请选择:");
        scanf("%d",&op);
    }while(op<0||op>3);
    return op?true:false;
}

int main()
{
    int op;
    char file1[32],file2[32];
    CHuffMan work;
    char step[2];
    while(Menu(op)){
        switch(op){
        case 1:
            printf("请输入待压缩文件名(.txt):");
            scanf("%s",file1);
            printf("请输入压缩文件名(.huf):");
            scanf("%s",file2);

soufail_charcount=0;
            FILE *fp1;
            fp1=fopen(file1,"rb");
            while(true){                         
                   fgetc(fp1);
                soufail_charcount++;                        
                  if(feof(fp1)) break;                
            }
            fclose(fp1);

work.Clear();
            work.EnCodeFile(file1,file2);
            printf("源文件长度:\t%d\n",work.GetSouceLen());
            printf("目标文件长度:\t%d\n",work.GetTargetLen());
            break;
        case 2:
            printf("请输入待解压文件名(.huf):");
            scanf("%s",file1);
            printf("请输入解压后文件名(.txt):");
            scanf("%s",file2);
            work.Clear();
            work.DeCodeFile(file1,file2);
            printf("源文件长度:\t%d\n",work.GetSouceLen());
            printf("目标文件长度:\t%d\n",work.GetTargetLen());
            break;
        case 3:
            work.GetHuffCode();
            break;
        }
        puts("按任意键继续...");
        gets(step);
        gets(step);
    }
    return 0;
}

使用哈夫曼编码实现文件压缩__win10,c++相关推荐

  1. 哈夫曼编码与文件压缩

    一:哈夫曼树与哈夫曼编码 大家知道,文件压缩的原理么? 假如我们有一个文件,文件当中仅有 A.B.C.D.E 五种字符,这五种字符出现的频率分别为 5次.4次.3次.2次.1次. 我们知道每个英文字母 ...

  2. 使用哈夫曼编码进行文件压缩

    给你一个图片文件,要求对其进行无损压缩, 看看压缩效果如何. 思路:读取文件-> 得到赫夫曼编码表 -> 完成压缩 将前面压缩的文件,重新恢复成原来的文件. 思路:读取压缩文件(数据和赫夫 ...

  3. 基于哈夫曼算法的文件压缩软件

    数据结构课设(一) 作业要求 1.设计并实现一个使用哈夫曼算法对文件进行压缩的工具软件. 2.通过命令行参数指定操作模式(压缩/解压).源文件名.目标文件名. 3.压缩操作将源文件按字节读入并统计字节 ...

  4. 基于哈夫曼编码对文件进行压缩和解压缩(详细讲解)

    基于哈夫曼编码对文件进行压缩和解压缩(详细讲解) 本文对应c++代码实现链接 一.背景 利用特定的算法来压缩数据的工具,压缩后生成的文件称为压缩包.如果想使用其中的数据,就得用压缩软件对数据进行解压. ...

  5. 哈夫曼编码解压缩文件 - Java实现

    文章目录 前言 一.文件压缩 二.文件解压 结语 前言 不了解哈夫曼树的可以移步查看我的另一篇博客:哈夫曼树(最优二叉树) 使用哈夫曼编码压缩文件,其实就是将每个字符的哈夫曼编码得到,每8位转为一个字 ...

  6. 哈夫曼树实现文件压缩

    最近在学了哈夫曼树之后,作为练习,写了一个文件压缩的小项目: 在这里和大家分享一下: 主要实现思路: 利用哈夫曼树的特性对字符进行哈夫曼编码,其中运用到了最小堆:利用最小堆的特性,找出构造哈夫曼树的结 ...

  7. 哈夫曼编码实现文件的压缩和解压

    哈夫曼编码的概念 哈夫曼编码是基于哈夫曼树实现的一种文件压缩方式. 哈夫曼树:一种带权路径最短的最优二叉树,每个叶子结点都有它的权值,离根节点越近,权值越小(根节点权值为0,往下随深度增加依次加一), ...

  8. 基于哈夫曼编码完成的文件压缩及解压

    这几天在较为认真的研究基于哈夫曼编码的文件压缩及解压,费了点时间,在这分享一下: 这里用链式结构,非顺序表结构: 文件压缩: 1.获取文件信息(这里采用TXT格式文本): 2.压缩文件: 3.写配置文 ...

  9. c语言用赫夫曼编码压缩文件,用哈夫曼编码C语言实现文件压缩

    用哈夫曼编码实现文件压缩,C语言编写,简单实用, if(j%8!=0) /*按八位读取*/ { for(f=j%8;f<8;f++) strcat(header[i].bits,"0& ...

最新文章

  1. mysql密码设置 alert_MySQL用户、权限及密码操作
  2. string之substring的用法
  3. 菜鸟学Linux 第090篇笔记 corosync+drbd+mysql
  4. 【深度学习】医学图像处理之视杯视盘分割调研分析
  5. 浅谈postMessage跨域通信与localStorage实现跨域共享
  6. matlab mbuild setup,关于mbuild的一个问题
  7. 记录处理搜狗新闻分类
  8. python字典顺序遍历_在Python中,如何按已排序的键顺序遍历字典?
  9. Warning: Multiple build commands for output file /xxx
  10. 用机器指令和汇编指令编程(修改版)
  11. 在 react 里写 vue3 ? 还写了自定义 hooks和 Hoc 构建了响应式 !
  12. 基于java 企业进销存管理系统设计(含源文件)
  13. 基于JFinal框架开发的企业办公系统-JFinalOA v1.1源码
  14. linux下Java环境的配置
  15. 对开发者有用的英文网站合集,建议收藏!
  16. linux SHELL下替代sed、ask的常用字符串处理(截取,判断、替换)
  17. 12_电话拨号器_界面实现
  18. c语言函数.pdf文档,c语言库函数表.pdf
  19. 牛腩新闻发布系统-发布
  20. 你不得不读的书籍清单

热门文章

  1. linux制作win10安装u盘,win10u盘安装系统怎样全新安装
  2. C4D云渲染哪个平台好?
  3. 微信开发专题---7微信公众号订阅号与服务号的区别
  4. css实现背景色渐变效果
  5. 华为智慧屏 鸿蒙如何获得,华为智慧屏鸿蒙UI系统获得2020德国红点设计大奖
  6. 电脑黑屏只有鼠标箭头
  7. vs2015 cordova 检测不到 node.js依赖项的问题
  8. Ubuntu 交叉编译国产开发板香橙派的环境配置
  9. 支持集显的服务器cpu,核显作用有多大? 七款无核显CPU推荐
  10. C语言双人游戏五子棋