文件压缩
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:\n"); 20 printf("1.压缩文件\n"); 21 printf("2.解压文件\n"); 22 printf("3.退出!\n"); 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("文件名输入错误,文件不存在!\n"); 55 return; 56 } 57 printf("请输入目标文件名:"); 58 scanf("%s",outfile); 59 ofp=fopen(outfile,"wb"); 60 if(ofp==NULL) 61 { 62 printf("文件名输入错误,文件不存在!\n"); 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\n", duration1 );*/ 119 printf("编码完成,是否查看编码信息: y or n?\n"); 120 c=getch(); 121 if(c=='y') 122 { printf("\n"); 123 printf("叶子数为%d,结点数为%d\n",n,m); 124 for(i=0;i<n;i++) 125 printf("%d号叶子结点的权值为:%ld,双亲为:%d,左右孩子:%d,编码为:%s\n", 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个字节\n",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("\n"); 203 finish2=clock(); 204 duration2=(double)(finish2- start2) / CLOCKS_PER_SEC; 205 /*printf( "写入目标文件用时为:%f seconds\n", duration2);*/ 206 printf( "压缩用时为:%f seconds\n", duration1+duration2); 207 speed=(float)FileLength/(duration1+duration2)/1000; 208 printf("\n压缩速率为:%5.2f KB/S\n",speed); 209 printf("\n"); 210 printf("源文件长度为:%ld个字节\n",FileLength); 211 sumlength=sumlength+4+n*2+count; //计算压缩后文件的长度 212 printf("压缩后文件长度为:%ld个字节\n",sumlength); 213 rate=(float)sumlength/(float)FileLength; 214 printf("压缩率(百分比)为:%4.2f%%%\n",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]='\0'; //编码结束符 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("文件名输入错误,文件不存在!\n"); 285 return; 286 } 287 printf("请输入目标文件名:"); 288 scanf("%s",outfile); 289 ofp=fopen(outfile,"wb"); 290 if(ofp==NULL) 291 { 292 printf("文件名输入错误,文件不存在!\n"); 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("\n解码信息:源文件长度为%d个字节,字符种类n=%d\n",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( "\n解压完成,解压用时为:%f seconds\n", 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个字节\n",FileLength);*/ 370 printf("\n解压速度为:%5.2fKB/S\n",speed); 371 372 fclose(ifp); 373 fclose(ofp); 374 return; 375 }
效果图: