• 文件压缩


      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <stdlib.h>
      4 #include <conio.h>
      5 #include <time.h>
      6 struct node{
      7     long weight;  //权值
      8     unsigned char ch;//字符
      9     int parent,lchild,rchild;
     10     char code[256];//编码的位数最多为256位
     11     int CodeLength;//编码长度
     12 }hfmnode[512];
     13 void compress();
     14 void uncompress(); 
     15 //主函数
     16 int main()
     17 {
     18     int choice;
     19      printf("请选择1~3:
    ");
     20      printf("1.压缩文件
    ");
     21      printf("2.解压文件
    ");
     22      printf("3.退出!
    ");
     23         scanf("%d",&choice);
     24         if(choice==1)compress();
     25         else if(choice==2)uncompress();
     26              else if(choice==3) return 0;
     27                   else printf("输入错误!");
     28     
     29 }
     30 
     31 //压缩函数
     32     void compress()
     33     {
     34         int i,j;
     35         char infile[20],outfile[20];
     36         FILE  *ifp,*ofp; 
     37         unsigned char c;//
     38         long FileLength,filelength=0;
     39         int n,m;//叶子数和结点数
     40         int s1,s2; //权值最小的两个结点的标号
     41         char codes[256];
     42         long sumlength=0;
     43         float rate,speed;
     44         int count=0;
     45         clock_t start1, start2,finish1,finish2;
     46         double  duration1,duration2;
     47     void encode(struct node *nodep,int n);//编码函数
     48     int select(struct node *nodep,int pose);//用于建哈弗曼树中选择权值最小的结点的函数
     49         printf("请输入要压缩的文件名:");
     50         scanf("%s",infile);
     51         ifp=fopen(infile,"rb");
     52         if(ifp==NULL)
     53         {
     54             printf("文件名输入错误,文件不存在!
    ");
     55             return;
     56         }
     57         printf("请输入目标文件名:");
     58           scanf("%s",outfile);
     59         ofp=fopen(outfile,"wb");
     60         if(ofp==NULL)
     61         {
     62            printf("文件名输入错误,文件不存在!
    ");
     63             return;
     64         }
     65 
     66 start1=clock() ;//开始计时1
     67 
     68     //统计文件中字符的种类以及各类字符的个数
     69     //先用字符的ASCII码值代替结点下标
     70         FileLength=0;
     71         while(!feof(ifp)) 
     72         {
     73          fread(&c,1,1,ifp);
     74          hfmnode[c].weight++; 
     75          FileLength++;
     76         }
     77         FileLength--; //文件中最后一个字符的个数会多统计一次,所以要减一
     78         hfmnode[c].weight--;
     79         //再将ASCII转换为字符存入到结点的ch成员里,同时给双亲、孩子赋初值-1
     80             n=0;
     81             for(i=0;i<256;i++)
     82                 if(hfmnode[i].weight!=0)
     83                 {
     84                     hfmnode[i].ch=(unsigned char)i;
     85                       n++;//叶子数
     86                 hfmnode[i].lchild=hfmnode[i].rchild=hfmnode[i].parent=-1;        
     87                 } 
     88 m=2*n-1;//哈弗曼树结点总数
     89  j=0;
     90  for(i=0;i<256;i++)//去掉权值为0的结点
     91      if(hfmnode[i].weight!=0)
     92      {
     93         hfmnode[j]=hfmnode[i];
     94         j++;
     95      }
     96 
     97 for(i=n;i<m;i++)//初始化根结点
     98 {
     99         hfmnode[i].lchild=hfmnode[i].rchild=-1;
    100         hfmnode[i].parent=-1;
    101 }
    102 //建立哈弗曼树
    103    for(i=n;i<m;i++)
    104    {
    105           s1=select(hfmnode,i-1);
    106         hfmnode[i].lchild=s1;
    107         hfmnode[s1].parent=i;
    108              s2=select(hfmnode,i-1);
    109         hfmnode[i].rchild=s2;
    110         hfmnode[s2].parent=i;
    111       hfmnode[i].weight=hfmnode[s1].weight+hfmnode[s2].weight;
    112    }
    113 //编码
    114 encode(hfmnode,n);
    115 
    116  finish1=clock();
    117 duration1=(double)(finish1- start1) / CLOCKS_PER_SEC;
    118  /*printf( "哈弗曼树编码用时为:%f seconds
    ", duration1 );*/
    119   printf("编码完成,是否查看编码信息: y or n?
    ");
    120         c=getch();
    121         if(c=='y')
    122         {    printf("
    ");
    123             printf("叶子数为%d,结点数为%d
    ",n,m); 
    124             for(i=0;i<n;i++)
    125             printf("%d号叶子结点的权值为:%ld,双亲为:%d,左右孩子:%d,编码为:%s
    ",
    126                 i,hfmnode[i].weight,hfmnode[i].parent,hfmnode[i].lchild,hfmnode[i].code);
    127         }    
    128 start2=clock() ;//开始计时2
    129      fseek(ifp,0,SEEK_SET);//将ifp指针移到文件开头位置
    130      fwrite(&FileLength,4,1,ofp);//将FileLength写入目标文件的前4个字节的位置
    131      fseek(ofp,8,SEEK_SET);//再将目标文件指针ofp移到距文件开头8个字节位置
    132    codes[0]=0;
    133     //将编码信息写入目标文件
    134      while(!feof(ifp))
    135      {
    136          fread(&c,1,1,ifp);
    137          filelength++;
    138          for(i=0;i<n;i++)
    139            if(c==hfmnode[i].ch) break;    //ch必须也为unsigned 型
    140          strcat(codes,hfmnode[i].code);
    141           while(strlen(codes)>=8)
    142           {
    143               for(i=0;i<8;i++)//将codes的前8位01代码表示的字符存入c
    144               {
    145                   if(codes[i]=='1')
    146                       c=(c<<1)|1;
    147                   else c=c<<1;
    148              }
    149             fwrite(&c,1,1,ofp); //将新的字符写入目标文件
    150             sumlength++;
    151             strcpy(codes,codes+8);//更新codes的值
    152           }
    153        if(filelength==FileLength) break;
    154      }
    155 
    156        //再将剩余的不足8位的01代码补全8位,继续写入
    157        if(strlen(codes)>0)
    158        {
    159            strcat(codes,"00000000");
    160            for(i=0;i<8;i++)
    161            {
    162               if(codes[i]=='1') 
    163                   c=(c<<1)|1;
    164               else c=c<<1;
    165            }
    166            fwrite(&c,1,1,ofp);
    167            sumlength++;
    168        }
    169      
    170        sumlength+=8;
    171 printf("编码区总长为:%ld个字节
    ",sumlength-8);
    172      //将sumlength和n的值写入目标文件,为的是方便解压
    173         fseek(ofp,4,SEEK_SET);
    174         fwrite(&sumlength,4,1,ofp);//把sumlength写进目标文件的第5-8个字节里         
    175         fseek(ofp,sumlength,SEEK_SET);
    176         fwrite(&n,4,1,ofp);//把叶子数n写进编码段后面的4个字节的位置
    177     //为方便解压,把编码信息存入n后面的位置
    178     //存储方式为:n*(字符值(1个字节)+该字符的01编码的位数(1个字节)+编码(字节数不确定,用count来计算总值))
    179         for(i=0;i<n;i++)
    180         {
    181            fwrite(&(hfmnode[i].ch),1,1,ofp);
    182            c=hfmnode[i].CodeLength;//编码最长为256位,因此只需用一个字节存储
    183            fwrite(&c,1,1,ofp);
    184            //写入字符的编码
    185            if(hfmnode[i].CodeLength%8!=0) 
    186               for(j=hfmnode[i].CodeLength%8;j<8;j++)//把编码不足8位的在低位补0,赋值给C,再把C写入
    187               strcat(hfmnode[i].code,"0");
    188            while(hfmnode[i].code[0]!=0)//开始存入编码,每8位二进制数存入一个字节
    189            {
    190                c=0;
    191                for(j=0;j<8;j++)
    192                {
    193                    if(hfmnode[i].code[j]=='1')
    194                         c=(c<<1)|1;
    195                    else c=c<<1;
    196                }
    197                strcpy(hfmnode[i].code,hfmnode[i].code+8);//编码前移8位,继续存入编码
    198                count++; //编码占的字节数的总值
    199                fwrite(&c,1,1,ofp);
    200            }
    201         }
    202         printf("
    ");
    203          finish2=clock();
    204          duration2=(double)(finish2- start2) / CLOCKS_PER_SEC;
    205          /*printf( "写入目标文件用时为:%f seconds
    ", duration2);*/
    206          printf( "压缩用时为:%f seconds
    ", duration1+duration2);
    207            speed=(float)FileLength/(duration1+duration2)/1000;
    208            printf("
    压缩速率为:%5.2f KB/S
    ",speed);
    209         printf("
    ");
    210         printf("源文件长度为:%ld个字节
    ",FileLength);
    211         sumlength=sumlength+4+n*2+count; //计算压缩后文件的长度
    212         printf("压缩后文件长度为:%ld个字节
    ",sumlength);
    213         rate=(float)sumlength/(float)FileLength;
    214         printf("压缩率(百分比)为:%4.2f%%%
    ",rate*100);
    215         fclose(ifp);
    216         fclose(ofp);  
    217         return;    
    218 }
    219 //返回书签
    220 
    221 //建立哈弗曼树中用于选择最小权值结点的函数
    222 int select(struct node *nodep,int pose)
    223 { 
    224     int i;
    225     int s1;
    226     long min=2147483647;//s初值为long型的最大值
    227     for(i=0;i<=pose;i++)
    228     {
    229         if(nodep[i].parent!=-1)continue;
    230         if(nodep[i].weight<min)
    231         {
    232             min=nodep[i].weight;  
    233             s1=i;
    234         }
    235     }
    236     return s1;
    237 }
    238 //返回书签
    239 
    240 //哈弗曼编码函数
    241 void encode(struct node *nodep,int n)
    242 {   //从叶子向根求每个字符的哈弗曼编码
    243     int start;
    244     int i,f,c;
    245     char codes[256];
    246     codes[n-1]='';  //编码结束符
    247     for(i=0;i<n;i++) //逐个字符求哈弗曼编码
    248     {
    249        start=n-1;
    250        for(c=i,f=nodep[i].parent;f!=-1;c=f,f=nodep[f].parent)
    251        {
    252            start--;
    253            if(nodep[f].lchild==c)
    254                codes[start]='0';
    255            else codes[start]='1';
    256            
    257        }
    258        strcpy(nodep[i].code,&codes[start]);
    259        nodep[i].CodeLength=strlen(nodep[i].code);  
    260     }
    261 }
    262 //返回书签
    263 
    264 //解压函数
    265 void uncompress() //解压文件
    266 
    267 { 
    268     clock_t start, finish;
    269     double  duration;
    270     FILE *ifp,*ofp;  
    271     char infile[20],outfile[20];
    272     long FileLength,sumlength,filelength;
    273     int n,m;  
    274     int i,j,k;
    275     char buf[256],codes[256];
    276     unsigned char c;
    277     int maxlength;
    278     float speed;
    279      printf("请输入要解压的文件名:");
    280         scanf("%s",infile);
    281         ifp=fopen(infile,"rb");
    282         if(ifp==NULL)
    283         {
    284         printf("文件名输入错误,文件不存在!
    ");
    285         return;
    286         }
    287      printf("请输入目标文件名:");
    288           scanf("%s",outfile);
    289         ofp=fopen(outfile,"wb");
    290         if(ofp==NULL)
    291         {
    292         printf("文件名输入错误,文件不存在!
    ");
    293         return;
    294         }
    295 start=clock() ;//开始计时
    296     fread(&FileLength,4,1,ifp);//从压缩文件读出FileLength、sumlength
    297     fread(&sumlength,4,1,ifp);
    298     fseek(ifp,sumlength,SEEK_SET);    //利用sumlength读出n的值
    299     fread(&n,4,1,ifp);
    300     printf("
    解码信息:源文件长度为%d个字节,字符种类n=%d
    ",FileLength,n);
    301     for(i=0;i<n;i++)//读结点信息
    302     {
    303        fread(&hfmnode[i].ch,1,1,ifp);//字符
    304        fread(&c,1,1,ifp);//编码长度
    305        hfmnode[i].CodeLength=c;
    306        hfmnode[i].code[0]=0;
    307        if(hfmnode[i].CodeLength%8>0) m=hfmnode[i].CodeLength/8+1;//m为编码占的字节数
    308        else m=hfmnode[i].CodeLength/8;
    309        for(j=0;j<m;j++)//根据字节长度m读出编码
    310        {
    311            fread(&c,1,1,ifp);//此处c为01编码转换成的字符
    312            itoa(c,buf,2);//字符型编码转换成二进制型(首位为1)
    313           //如果编码不够8位,则说明缺少了8-k位0,因此应先在前面空缺位写0
    314            for(k=8;k>strlen(buf);k--)
    315            {
    316                strcat(hfmnode[i].code,"0");
    317            }
    318            //再把二进制编码存进hfmnode.code中
    319             strcat(hfmnode[i].code,buf);
    320        }
    321        hfmnode[i].code[hfmnode[i].CodeLength]=0;//去掉编码中多余的0  
    322     }
    323  //找出编码长度的最大值
    324  maxlength=0;
    325  for(i=0;i<n;i++)
    326      if(hfmnode[i].CodeLength>maxlength)
    327          maxlength=hfmnode[i].CodeLength;
    328 //开始写入目标文件
    329  fseek(ifp,8,SEEK_SET); //指针指向编码区,开始解码
    330  filelength=0;
    331  codes[0]=0;
    332  buf[0]=0;
    333  while(1)
    334  {
    335     while(strlen(codes)<maxlength)//codes小于编码长度的最大值时,继续读码
    336     {
    337         fread(&c,1,1,ifp);
    338         itoa(c,buf,2);//还原编码
    339         for(k=8;k>strlen(buf);k--)
    340         {
    341            strcat(codes,"0");//把缺掉的0补上
    342         }
    343         strcat(codes,buf);//codes中此时存的为一串01编码
    344     }
    345     for(i=0;i<n;i++)
    346     {  //在codes中查找能使其前weight位和hfmnode.code相同的i值,weight即为codelength
    347       if(memcmp(hfmnode[i].code,codes,(unsigned int)hfmnode[i].CodeLength)==0) break;
    348     }
    349     strcpy(codes,codes+hfmnode[i].CodeLength);//更新codes的值
    350     c=hfmnode[i].ch;
    351     fwrite(&c,1,1,ofp);
    352 
    353     filelength++;
    354     if(filelength==FileLength) break;//写入结束
    355  }
    356      finish = clock();
    357      duration = (double)(finish - start) / CLOCKS_PER_SEC;
    358      printf( "
    解压完成,解压用时为:%f seconds
    ", duration );
    359        
    360      fseek(ifp,0,SEEK_SET);  
    361      FileLength=0;
    362         while(!feof(ifp)) 
    363         {
    364           fread(&c,1,1,ifp);
    365           FileLength++;
    366         }
    367         FileLength--; 
    368     speed=(float)FileLength/duration/1000;
    369     /*printf("此文件长度为:%ld个字节
    ",FileLength);*/
    370     printf("
    解压速度为:%5.2fKB/S
    ",speed);
    371 
    372    fclose(ifp);
    373    fclose(ofp);
    374    return;
    375 }

    效果图:

  • 相关阅读:
    【转】直方图中bins的理解及处理
    [LeetCode] 1. Two Sum
    数据结构课学到的常用知识总结概括
    Java核心技术第三章数据类型
    mysql 出现You can't specify target table for update in FROM clause错误的解决方法
    mysql 从一个表中查数据并插入另一个表实现方法
    使用ECharts,绘制柱状图
    mysql 查询1小时内_mysql查询一个小时之内的数据
    mysql查询表中所有字段的名字
    MySQL 时间函数总集
  • 原文地址:https://www.cnblogs.com/joyeehe/p/8078963.html
Copyright © 2020-2023  润新知