#include<stdio.h> #include<iostream> #include<conio.h> #include<stdlib.h> #include<string.h> #include<stack> #include<queue> using namespace std; struct HuffNode { long long weight; int parent, lchild, rchild; }; struct HuffTree { HuffNode *buffer; int leafnum; // buffer的长度为2*leafnum - 1 }; struct BitBuffer { int buffer, count; }; int InitBitBuffer( BitBuffer &B ) // 初始化一个位流缓冲区 { B.buffer = B.count = 0; return 1; } bool AppendBit( BitBuffer &B, int bit) // 向位流缓冲区中追加一位 { if (bit==1) B.buffer = (B.buffer << 1) | 1;//如果bit为1,buffer左移一位并与1位或,也就是最右面添1 else B.buffer = B.buffer << 1; B.count++; return B.count>=8; // 如果已填充满了8位,返回真 } int countfre(FILE *fp,long long *&cal)//进行文件字节频率统计 { int ch; while((ch=fgetc(fp))!=EOF) cal[ch]++; return 1; } //创建哈夫曼树 int CreateHuffTree(HuffTree &T, int leafnum, long long weight[]) { // 初始化哈夫曼树的空间 T.buffer = new HuffNode[2*leafnum-1]; T.leafnum = leafnum; for (int i=0; i<leafnum; i++) { T.buffer[i].weight = weight[i]; T.buffer[i].parent = -1; T.buffer[i].lchild = -1; T.buffer[i].rchild = -1; } for (int i=0; i<leafnum-1; i++) {// 选出两棵根结点权值最小的树 int m1=-1, m2=-1; for (int j=0; j<leafnum+i; j++) {// 先判断buffer[j]是否是根,如果不是根,则跳过 if (T.buffer[j].parent==-1) { if (m1==-1 || T.buffer[j].weight<T.buffer[m1].weight) {//m2为次小的,m1为最小的下标 m2 = m1; m1 = j; } else if (m2==-1 || T.buffer[j].weight<T.buffer[m2].weight) m2 = j; } } // 创建一个新的根结点,将选出的两棵树分别挂在新根结点的左右子上 T.buffer[m1].parent = i+leafnum; T.buffer[m2].parent = i+leafnum; T.buffer[i+leafnum].parent = -1; if(m1<m2)//小的为左子 { T.buffer[i+leafnum].lchild = m1; T.buffer[i+leafnum].rchild = m2; } else { T.buffer[i+leafnum].lchild = m2; T.buffer[i+leafnum].rchild = m1; } T.buffer[i+leafnum].weight = T.buffer[m1].weight + T.buffer[m2].weight; } return 1; } //使用哈夫曼编码对文件进行重编码,并输出新文件 int coding(HuffTree T, FILE *fp, FILE *outf) { //把哈夫曼树写入文件 for(int i=0;i<511;i++) { int byte=T.buffer[i].parent-256; fputc(byte,outf); } //在第512个字节的位置加入空字节 fputc(0,outf); //根据静态三叉链构成的哈夫曼树的结构,前buffer[0到leafnum-1]这leafnum个数据中存有叶子结点,根据读到的数据调用对应位置的buffer[]即可 int ch; BitBuffer B;//位流 InitBitBuffer(B); stack<int> temp;//用来存放每个字节对应的哈夫曼编码 while((ch=fgetc(fp))!=EOF) {//读取fp文件中的字节 while(T.buffer[ch].parent!=-1) { if(ch==T.buffer[T.buffer[ch].parent].lchild) temp.push(0); else if(ch==T.buffer[T.buffer[ch].parent].rchild) temp.push(1); ch=T.buffer[ch].parent; } while(!temp.empty()) {//位流转换为字节流并输出到文件中 if(AppendBit(B,temp.top())==1) { fputc(B.buffer,outf); InitBitBuffer(B); } temp.pop(); } } //处理buffer没到8位的情况 for(int i=B.count;i<8;i++) B.buffer = B.buffer << 1;//最后追加0 fputc(B.buffer,outf); //把B.count放入文件开始预留的位置 fseek(outf,511L,0); fputc(B.count,outf); return 1; } int decompress(char arg[][32]) { //还原哈夫曼树 int count; FILE *fp; if((fp=fopen(arg[0],"rb"))==NULL) { cout << "文件打开错误,请检查后重新执行。"; return 1; } HuffTree T; int ch; T.buffer = new HuffNode[511]; T.leafnum = 256; for (int i=0; i<511; i++) {//初始化哈夫曼树 T.buffer[i].parent = -1; T.buffer[i].lchild = -1; T.buffer[i].rchild = -1; } for(int i=0;i<510;i++) { ch=fgetc(fp); T.buffer[i].parent=ch+256; if(T.buffer[ch+256].lchild==-1) T.buffer[ch+256].lchild=i; else T.buffer[ch+256].rchild=i; } fgetc(fp); count=fgetc(fp); //解码写到文件 FILE *outf=fopen(arg[1],"wb"); if(outf==NULL) { cout << "创建新文件错误,请检查输入后重新执行。"; return 2; } int count2=0; queue<int> temp; stack<bool> s;//用来存储十进制转换为二进制的数 int tran=510; while(1) { if((ch=fgetc(fp))!=EOF) {temp.push(ch); count2++;} else break; //count2大于等于2的时候就可以写入文件了,证明没到末尾 while(count2>=2)//每次都会剩下一个未被处理,这个就是最后那个不完整的 { int num=temp.front(); temp.pop(); count2--; for(int i=0;i<8;i++) {//十转二 s.push(num%2); num/=2; } while(!s.empty()) { if(T.buffer[tran].lchild==-1 && T.buffer[tran].rchild==-1) { fputc(tran,outf); tran=510; } else if(s.top()==0) { tran=T.buffer[tran].lchild; s.pop(); } else if(s.top()==1) { tran=T.buffer[tran].rchild; s.pop(); } }//while } }//处理最后不满8位的情况 int num=temp.front(); temp.pop(); for(int i=0;i<8;i++) {//十转二 s.push(num%2); num/=2; } if(T.buffer[tran].lchild==-1 && T.buffer[tran].rchild==-1) { fputc(tran,outf); tran=510; } for(int i=0;i<count+1;i++) { if(s.top()==0) { tran=T.buffer[tran].lchild; s.pop(); } else if(s.top()==1) { tran=T.buffer[tran].rchild; s.pop(); } if(T.buffer[tran].lchild==-1 && T.buffer[tran].rchild==-1) { fputc(tran,outf); tran=510; } } fclose(outf); cout<<"解压缩成功!/n"; return 1; } int compress(char arg[][32])//压缩函数 { //根据参数创建文件,并判断是否能成功创建 FILE *fp; if((fp=fopen(arg[0],"rb"))==NULL) { cout << "文件打开错误,请检查后重新执行。/n"; return 1; } FILE *outf=fopen(arg[1],"wb+"); if(outf==NULL) { cout << "创建新文件错误,请检查输入后重新执行。/n"; return 2; } //进行频率统计 long long *cal=new long long[256]; for(int i=0;i<256;i++) cal[i]=0; countfre(fp,cal); fclose(fp); //根据统计数据,建立哈夫曼树 HuffTree T; CreateHuffTree(T,256,cal); delete []cal; //根据哈夫曼树,对要压缩的文件进行重新编码 fp=fopen(arg[0],"rb"); coding(T,fp,outf); fclose(fp); fclose(outf); cout<<"压缩成功/n"; return 1; } int main(int argc, char *argv[]) { int choose; char arg[2][32]; while(1) { cout<<"/n请选择: 1.压缩 2.解压缩 0.退出/n"; cin>>choose; switch (choose) { case 0 :return 0; case 1 : cout<<"请输入要压缩的文件名:"; cin>>arg[0]; cout<<"请输入压缩后的文件名:"; cin>>arg[1]; compress(arg); continue; case 2 : cout<<"请输入要解压的文件名:"; cin>>arg[0]; cout<<"请输入解压后的文件名:"; cin>>arg[1]; decompress(arg); continue; default:; } } return 0; }