考核实验中的一个,我也认为较为难的一个,其实也不是很难,只是有点复杂,只要分解成多个问题去解决就好了
比如你要知道Huffman是怎样对文件进行编码和解码的
然后需要知道怎么去建Huffman二叉树,建好了Huffman树 然后就是对其进行编码 最后是解码
只要把每个过程弄清楚了,用几天时间(对本科生而言)写出来应该还是有可能的
下面的代码是某位同学写的,经过其本人允许我决定贴到我的博客中供大家学习,但是如果按照源代码来演示的话,你懂得……
期中要编码的txt文件必须放在.cpp源文件同个文件夹下,所以大家在写的过程中要注意。。。
当然,这个代码的漏洞也有很多,欢迎大家来吐槽。。。
/* 功能:Huffman对文件进行编码和解码 时间:2014-11-24 */ #include<stdio.h> #include<stdlib.h> #include<string.h> //结点结构体 typedef struct Node { unsigned char value; //结点值 int frequency; //结点频数 struct Node *Lchild; //左儿子 struct Node *Rchild; //右儿子 }; //编码字符结构体 typedef struct { unsigned char value; //字符值 char code[100]; //字符编码 int frequency; // 字符频数 int codelen; //字符编码长度 }HuffmanCode; struct Node *root; //哈弗曼树根节点 //读取文件统计各个字母出现频率,返回字符种数 //filename为文件名,*node为用于保存数据的数组 int count(char filename[],HuffmanCode *node) { FILE *fp=fopen(filename,"rb"); //打开文件 unsigned char now; //读取的当前字符 int num = 0; //字符种数 fread(&now, sizeof(unsigned char), 1, fp); //读取字符 while(!feof(fp)) //未读到文件尾 { int i; //重复字符 for (i = 0; i < num; i++) { if (now == node[i].value) { node[i].frequency++; break; } } //未重复字符 if (i==num) { node[i].value = now; node[i].frequency=1; num++; } fread(&now, sizeof(unsigned char), 1, fp); //读取字符 } fclose(fp); return num; } //构造Huffman树 //*node为用于保存数据的数组,n为字符种数 struct Node* buildHuffman(HuffmanCode *node, int n) { struct Node **arr,*temp; //arr为造数用数组,temp为最终返回的树 arr = (struct Node**)malloc(n*sizeof(struct Node)); //初始化数组 for (int i = 0; i < n; i++) { arr[i] = (struct Node*)malloc(sizeof(struct Node)); arr[i]->value = node[i].value; arr[i]->frequency = node[i].frequency; arr[i]->Lchild = arr[i]->Rchild = NULL; } //建树 for (int i = 0; i < n-1; i++) { int m1=-1, m2; //最小的两个权值在数组中下标 //将m1,m2指向头两棵树 for (int j = 0; j < n; j++) { if (m1 == -1 && arr[j] != NULL) { m1 = j; continue; } if (arr[j] != NULL) { m2 = j; break; } } //比较权值大小,找到最小的两个 for (int j = m2; j < n; j++) { if (arr[j]!=NULL && arr[j]->frequency < arr[m1]->frequency) { m2 = m1; m1 = j; } else if (arr[j] != NULL && arr[j]->frequency < arr[m2]->frequency) m2 = j; } //将两个权值最小的构成新树 temp = (struct Node*)malloc(sizeof(struct Node)); temp->Lchild = arr[m1]; temp->Rchild = arr[m2]; temp->frequency = arr[m1]->frequency + arr[m2]->frequency; temp->value = NULL; //将新树加入数组 arr[m1] = temp; arr[m2] = NULL; } free(arr); return temp; //最终temp为树根 } //Huffman编码 //*node为根节点,len为当前编码长度,*arr为用于保存数据的数组,num为字符种数 void codeByHuffman(struct Node *node,int len,HuffmanCode *arr,int num) { static char code[100]; //编码 //左儿子不为空,编码为0 if (node->Lchild != NULL) { code[len] = '0'; code[len + 1] = '\0'; codeByHuffman(node->Lchild, len+1,arr,num); //对左儿子调用此函数 } //右儿子不为空,编码为1 if (node->Rchild != NULL) { code[len] = '1'; code[len + 1] = '\0'; codeByHuffman(node->Rchild, len + 1, arr, num); //对右儿子调用此函数 } //叶节点 else { for (int i = 0; i < num; i++) { //将编码复制给数组里的元素 if (arr[i].value == node->value) { strcpy(arr[i].code, code); arr[i].codelen = 0; for (int j = 0; arr[i].code[j] != '\0'; j++) arr[i].codelen++; return; } } } } //将文件内容转为Huffman编码 //sourceFile为原文件名,*arr为用于保存数据的数组,n为字符种数,aimFile为目标文件名 void writeCode(char sourceFile[],HuffmanCode *arr,int n,char aimFile[]) { FILE *fSource, *fAim; //原文件以及目标文件 unsigned char now;//读取的当前字符 unsigned char save = 0; //每次保存的一个字节长度 int len = 0; //每次保存的一个字节已存了多少长度 int totalLen = 0; //文件编码总长度 int last; //最后一次写入时的位数 //总字节长度 for (int i = 0; i < n; i++) totalLen = totalLen + arr[i].codelen*arr[i].frequency; //计算最后一次写入多少位 last = totalLen % 8; fSource = fopen(sourceFile, "rb"); fAim = fopen(aimFile, "wb"); //先将huffman编码输进文件,以">"作为标识符 用于译码 fwrite("->",1,2,fAim); fwrite(&n, sizeof(int), 1, fAim); //写入字符种数 fwrite(&last, sizeof(int), 1, fAim); //写入最后一次写入的位数 //写入个字符值和字符频数 for (int i = 0; i < n; i++) { fwrite(&arr[i].value, sizeof(unsigned char), 1, fAim); fwrite(&arr[i].frequency, sizeof(int), 1, fAim); } fread(&now, sizeof(unsigned char), 1, fSource); //读取源文件字符 while (!feof(fSource)) { for (int i = 0; 1; i++) { if (now == arr[i].value) { for(int j=0;j<arr[i].codelen;j++) { //按位操作保存编码 if(len!=0) save=save<<1; save=save|(arr[i].code[j]-'0'); len++; //一个字节已存满,写入文件 if(len==8) { fwrite(&save,sizeof(unsigned char),1,fAim); save=0; len=0; } } break; } } fread(&now, sizeof(unsigned char), 1, fSource); //读取字符 } //最后一次可能没写满一个字节也要写入文件 if(len!=0) fwrite(&save,sizeof(unsigned char),1,fAim); fclose(fSource); fclose(fAim); } //译码 //sourceFile为原文件名,*arr为用于保存数据的数组,aimFile为目标文件名 void writeDecode(char sourceFile[], HuffmanCode *arr, char aimFile[]) { FILE *fSource, *fAim; //原文件以及目标文件 int n; //字符种数 int last=0; //最后一次读取的位数 unsigned char now;//读取的当前字符 fSource = fopen(sourceFile, "rb"); //确认是否以此代码编码的Huffman for (int i = 0; i < 2;i++) { fread(&now, sizeof(char), 1, fSource); //读取字符 if (i == 0) { if (now == '-') continue; else { printf("原文件huffman编码格式不正确\n"); fclose(fSource); return; } } if (now == '>') break; else { printf("原文件huffman编码格式不正确\n"); fclose(fSource); return; } } // fread(&n, sizeof(int), 1, fSource); //字符种数 fread(&last, sizeof(int), 1, fSource); //最后一次读的位数 for (int i = 0; i < n; i++) { fread(&arr[i].value, sizeof(unsigned char), 1, fSource); //字符 fread(&arr[i].frequency, sizeof(int), 1, fSource);//字符频数 } struct Node *root = buildHuffman(arr, n); //建树 codeByHuffman(root, 0, arr, n); struct Node *pNow = root; //当前结点 unsigned char temp; //每次读1个字节 fAim = fopen(aimFile, "wb"); fread(&temp, sizeof(unsigned char), 1, fSource); while (!feof(fSource)) { unsigned char ifLast; //用于判断是否读到文件末尾,方便读取固定位数 fread(&ifLast, sizeof(unsigned char), 1, fSource); int i; if (feof(fSource)) i = last-1; else i = 7; for (; i >= 0; i--) { if ((temp>>i & 1)==0) pNow = pNow->Lchild; else pNow = pNow->Rchild; if (pNow->Lchild == NULL && pNow->Rchild == NULL) { fwrite(&pNow->value, sizeof(unsigned char), 1, fAim); pNow = root; } } temp=ifLast; } fclose(fSource); fclose(fAim); } void main() { while (1) { HuffmanCode node[2000]; int num; //字符种数 char Sfilename[100] = ""; //原文件名 char Afilename[100] = "";//输出文件名 char choose[3] = ""; printf("请选择:\n"); printf("\t1.编码\n"); printf("\t2.译码\n"); printf("\t3.退出\n"); scanf("%s", choose); fflush(stdin); if (!strcmp(choose, "1")) { while (1) { printf("请输入你想要编码的文件名:\n"); scanf("%s", Sfilename); fflush(stdin); FILE *fp = fopen(Sfilename, "rb"); if (fp == NULL) { printf("未找到文件.\n"); continue; } else { fclose(fp); break; } } printf("请输入编码后的文件名:\n"); scanf("%s", Afilename); fflush(stdin); num = count(Sfilename, node); //统计 root = buildHuffman(node, num); //建树 codeByHuffman(root, 0, node, num);//各个字符的Huffman编码 writeCode(Sfilename, node, num, Afilename); //输出Huffman编码文件 printf("编码完成!\n\n"); } else if (!strcmp(choose, "2")) { while (1) { printf("请输入你想要译码的文件名:\n"); scanf("%s", Sfilename); fflush(stdin); FILE *fp = fopen(Sfilename, "rb"); if (fp == NULL) { printf("未找到文件.\n"); continue; } else { fclose(fp); break; } } printf("请输入译码后的文件名:\n"); scanf("%s", Afilename); fflush(stdin); writeDecode(Sfilename, node, Afilename); //译码并写文件 printf("译码完成!\n\n"); } else if (!strcmp(choose, "3")) return; else { printf("输入有误!\n"); continue; } } }
时间: 2024-10-03 21:55:56