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]='