哈夫曼压缩与解压代码实现
不会算法做不了逆向题....
这个哈夫曼压缩是我在网上找的代码,自己写了一下并对其作了微改,使其能处理unsigned char 类型,也就是说可以处理二进制文件,例如照片之类的..
//哈夫曼编码实现 二进制文件的压缩与解压 #include <stdio.h> #include <stdlib.h> #include <cstring> #include <iostream> #include <algorithm> using namespace std; struct HaffNode{ int times; //字符出现的次数 unsigned char data; //记录某种字符// struct HaffNode* leftChild; struct HaffNode* rightChild; }; struct HaffNode left_node[256]; //用于存储哈夫曼树的左子节点// struct HaffNode right_node[256]; //用于存储哈夫曼树的右子节点// char hfcode[256][50]; //用于存储不同字符的哈夫曼编码结果// void CreateHaffman(struct HaffNode* hf,int len){ //递归构造// if(len == 1){ return ; //数组长度为1时,停止// } sort(hf,hf + len,[=](struct HaffNode a,struct HaffNode b){ //出现次数从大到小排序// Lambda return a.times < b.times ? 0:1; }); left_node[len] = hf[len-2]; //从出现次数最小的开始合并// right_node[len] = hf[len-1]; struct HaffNode parent = {0}; parent.leftChild = &left_node[len]; parent.rightChild = &right_node[len]; parent.times = left_node[len].times + right_node[len].times; //左右子树权值和作为父节点 hf[len-2] = parent; //将最后两个最小的节点换成父节点// CreateHaffman(hf,len-1); //递归,直到len变为1// 此时的 hf数组就是hafffman树// } void HfEncode(struct HaffNode* hf,char *keepCode,int len) //哈夫曼编码 { if(hf->leftChild == NULL || hf->rightChild == NULL){ //叶子节点,存储 keepCode[len] = '\0'; //终止// strcpy(hfcode[hf->data],keepCode); return ; } keepCode[len] = '0'; //遍历,扩展keepCode// HfEncode(hf->leftChild,keepCode,len+1); keepCode[len] = '1'; HfEncode(hf->rightChild,keepCode,len+1); } void debug(){ //输出打印哈夫曼编码// for(int i = 0;i<=255;i++){ if(strlen(hfcode[i]) == 0){ continue; }else{ printf("%hhu : %s\n",i,hfcode[i]); } } } struct HaffNode * haff_Decode(struct HaffNode* node,int flag){ if(flag == 0){ return node->leftChild; } if(flag == 1){ return node->rightChild; } } int main(){ int cip_len = 0; //密文中数据长度// int count[256] = {0}; //记录不同Byte类型// int exist_len = 0; //密文中 存在的bytes 种类// struct HaffNode hf[256] = {0}; //创建哈夫曼数组节点// char keepCode[50]; //临时储存编码 // unsigned char redc; unsigned char All_Bytes[0x2000] = {0}; //记录,明文// FILE *fp = fopen("C:\\Users\\TLSN\\Desktop\\强国杯\\QG\\encrypt_new\\attachment_23092f4ec\\input.crypt","rb"); //读取数据// FILE *fp1 = fopen("C:\\Users\\TLSN\\Desktop\\强国杯\\QG\\encrypt_new\\attachment_23092f4ec\\aaa_save.ttt","wb"); //写入压缩的哈夫曼// int ji = 0; //不使用getc == EOF读取二进制的原因是,二进制文本中有的数据为0xff,恰巧是EOF对应的值 fseek(fp, 0, SEEK_END); int lenl = ftell(fp); fseek(fp,0,SEEK_SET); while(cip_len < lenl){ //逐个读取字符// redc = fgetc(fp); All_Bytes[cip_len] = redc; cip_len++; count[redc]++; //printf("%x : %x\n",ji++,redc); } for(int i = 0;i<256;i++){ //用 Haffman节点结构体记录密文文件中某种byte存在的个数// if(count[i] != 0){ hf[exist_len].times = count[i]; hf[exist_len].data = i; exist_len++; } } CreateHaffman(hf,exist_len); //构建哈夫曼树// 所有节点都被融合到了hf[0] 上// HfEncode(&hf[0],keepCode,0); //计算哈夫曼编码// //打印输出,debug// //debug(); //压缩存储// //存储采用8位 fseek(fp,0,0); //将指针置0// unsigned char ttt = 0; int ans = 0; int ziplen = 0; //压缩后的字节数// int jishuqi = 0; while(jishuqi < lenl){ redc = All_Bytes[jishuqi]; jishuqi++; for(int i = 0;i<strlen(hfcode[redc]) ;i++){ ttt |= hfcode[redc][i] - '0'; //让ttt和编码中的每一位进行或运算,8位一组// 这样就能得到 0000 0001 这样的形式// ans ++; if(ans == 8){ fwrite(&ttt,1,1,fp1); ziplen++; ttt = 0; ans = 0; }else{ ttt <<= 1; } } } //如果最后的编码不到8位,将ttt的数据移到最左边再存储// if(ans != 0){ ttt = ttt << (7 - ans); fwrite(&ttt,1,1,fp1); ziplen++; } fclose(fp); fclose(fp1); printf("开始哈夫曼的遍历......\n"); getchar(); //下面是哈夫曼解码// FILE *fp2 = fopen("C:\\Users\\TLSN\\Desktop\\强国杯\\QG\\encrypt_new\\attachment_23092f4ec\\aaa_save.ttt","rb"); //读压缩的哈夫曼// FILE *fp3 = fopen("C:\\Users\\TLSN\\Desktop\\强国杯\\QG\\encrypt_new\\attachment_23092f4ec\\aaa_save_cure.ttt","wb"); //读压缩的哈夫曼// struct HaffNode * hf_child = &hf[0]; unsigned char tt = 0; int N_num = 0; //明文的长度// unsigned char reder = 0; while(fread(&reder,1,1,fp2)){ unsigned char op = 128; for(int i = 0;i<8;i++){ tt = reder & op; //一位一位的取// reder = reder << 1; tt >>= 7; hf_child = haff_Decode(hf_child,tt); //把每一位都进行哈夫曼的遍历// if(hf_child->leftChild == NULL || hf_child->rightChild == NULL){ //找到子节点// fprintf(fp3,"%c",hf_child->data); N_num++; hf_child = &hf[0]; //重新遍历// } } } fclose(fp2); fclose(fp3); //这样编码的话,如果 压缩的时候编码不到8位 ,那解压的时候最后一两个字符会出错 return 0; } //参考 https://blog.csdn.net/Black_Will/article/details/72866725
__EOF__

本文作者:_TLSN
本文链接:https://www.cnblogs.com/lordtianqiyi/articles/16511727.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
本文链接:https://www.cnblogs.com/lordtianqiyi/articles/16511727.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现