利用哈夫曼树实现文件的压缩与解压缩
压缩:
1、统计出文件中相同字符出现的次数
2、获取哈夫曼编码
次数作为权值构建哈夫曼树
3、重新编码,写回压缩文件
保存头文件:
源文件后缀
编码信息的行数
每个字符的权
保存编码

解压缩:
1、获取原文件后缀
2、获取每个字符出现的次数,即权值
3、利用之前后的的权值,还原哈夫曼树
4、找到对应的叶子节点,将信息保存到解压文件中
在写压缩文件之前,首先需要实现堆和哈夫曼树
源代码戳这里
(https://coding.net/u/g33_N/p/fileCompress/git)

#define _CRT_SECURE_NO_DEPRECATE
#include"HuffManTree.h"
#include<assert.h>
struct FileInfo
{FileInfo():_count(0){}unsigned char _ch;//当前字符size_t _count;//当前字符出现的次数std::string _strCode;//当前字符的哈夫曼编码//重载+FileInfo operator+(const FileInfo& fileInfo){FileInfo ret(*this);ret._count += fileInfo._count;return ret;}//重载<bool operator<(const FileInfo& fileInfo)const{return _count<fileInfo._count;}//重载!=bool operator != (const FileInfo& fileInfo)const{return _count != fileInfo._count;}
};
class CompressFile
{
public:CompressFile(){for (size_t idx = 0; idx < 256; ++idx){_fileInfo[idx]._ch = idx;_fileInfo[idx]._count = 0;//每一个字符出现的次数初始化为0}}void FileCount(const std::string& strFileName){//统计字符出现的次数FILE* fOut = fopen(strFileName.c_str(), "r");//打开一个文件assert(fOut);unsigned char rBuf[1024];//存取读到的文件内容while (1){size_t rSize = fread(rBuf, 1, 1024, fOut);//返回从文件中读到的字节数if (rSize == 0)break;for (size_t idx = 0; idx < rSize; ++idx){_fileInfo[rBuf[idx]]._count++;//统计每个字符出现的次数}}}//获取编码信息void GetHuffManCode(){// 创建HuffManTreeHuffmanTree<FileInfo> ht(_fileInfo, sizeof(_fileInfo) / sizeof(_fileInfo[0]), FileInfo());_GetHuffManCode(ht.GetRoot());//获取哈夫曼编码}//文件压缩void FileCopress(const std::string& strFileName)//参数为要压缩的文件名{FileCount(strFileName);GetHuffManCode();unsigned char value = 0;//一个字节,保存8位编码unsigned char wBuff[1024];//保存编码信息int wSize = 0;FILE* fOut = fopen(strFileName.c_str(), "rb");assert(fOut);std::string fileName = GetFileName(strFileName.c_str());//获取文件名fileName += ".hzp";//给文件名加后缀(压缩后)FILE* fIn = fopen(fileName.c_str(), "wb");//打开一个文件写入压缩信息assert(fIn);unsigned char rBuf[1024];std::string strCode;int pos = 0;//保存编码信息std::string  strHead;strHead += GetPostFix(strFileName.c_str());//获取文件后缀strHead += '\n';char strLineCount[32];//编码信息的行数size_t lineCount = 0;//统计每个字符出现的次数 a:1size_t idx = 0;while (idx<256){if (_fileInfo[idx]._count){if(_fileInfo[idx]._ch!='\n')strCode += _fileInfo[idx]._ch;strCode += ':';_itoa(_fileInfo[idx]._count, strLineCount, 10);strCode += strLineCount;lineCount++;strCode += '\n';}idx++;}_itoa(lineCount, strLineCount, 10);strHead += strLineCount;//保存行数strHead += '\n';strHead += strCode;//保存编码信息fwrite(strHead.c_str(), 1, strHead.length(), fIn);//此处写入文件的大小一定是写入内容的实际大小,否则写入文件的内容之后会出现随机值fseek(fOut, 0, SEEK_SET);//跳到文件开头,之前已读到结尾//遍历编码,8位一字节写入Buff中while (1){size_t rSize = fread(rBuf, 1, 1024, fOut);if (rSize == 0)break;for (size_t idx = 0; idx < rSize; ++idx){std::string strCode = _fileInfo[rBuf[idx]]._strCode;for (size_t idx = 0; idx < strCode.length(); ++idx){value <<= 1;//若编码为0,左移一位if (strCode[idx] == '1')//若编码为1,value或1{value |= 1;}if (++pos == 8)//满8位,将value写入buff里{wBuff[wSize++] = value;if (wSize == 1024)//buff大小满1024写入文件{fwrite(wBuff, 1, 1024, fIn);wSize = 0;//写入文件之后将buff索引置0}pos = 0;//value写入buff后,pos置0value = 0;//value也置0,重新保存下一个字节}}}}if (pos < 8)//若编码读完后,pos<8,buff大小也不满1024,将编码左移至最高位,然后写入buff,最终写入文件{value <<= (8 - pos);wBuff[wSize++] = value;fwrite(wBuff, 1, wSize, fIn);}//关闭文件fclose(fOut);fclose(fIn);}//解压缩void UnCompressFile(const std::string& compressFile){FILE* fOut = fopen(compressFile.c_str(), "rb");//打开一个压缩文件assert(fOut);//获取源文件后缀std::string  strPostFix = ReadLine(fOut);//获取信息行数int LineCount = atoi(ReadLine(fOut).c_str());//获取压缩后的编码int idx = 0;while (idx < LineCount){std::string strCode = ReadLine(fOut);//读取每一行信息_fileInfo[(unsigned char )strCode[0]]._count = atoi(strCode.substr(2).c_str());//获取每个字符出现的次数,也就是权++idx;}//利用上边获取到的权还原哈夫曼树HuffmanTree<FileInfo> ht(_fileInfo, sizeof(_fileInfo) / sizeof(_fileInfo[0]), FileInfo());HuffmanTreeNode<FileInfo>* pRoot = ht.GetRoot();//获取哈夫曼树的根_GetHuffManCode(pRoot);//获取哈夫曼编码std::string fileName = GetFileName(compressFile.c_str());//获取文件名std::string strFix = GetPostFix(compressFile.c_str());//文件后缀fileName += strPostFix;//解压缩后文件名及后缀FILE* fIn = fopen(fileName.c_str(), "wb");// 打开一个文件写入解压缩后的信息assert(fIn);unsigned char rBuff[1024];int pos = 8;unsigned char wBuff[1024];size_t wSize = 0;long long FileSize = pRoot->_weight._count;//压缩信息的大小HuffmanTreeNode<FileInfo>* pCur = pRoot;//解压缩while (1&&pCur){int rSize = fread(rBuff, 1, 1024, fOut);if (rSize == 0)return;for (int idx = 0; idx < rSize; ++idx){while (1){--pos;if (rBuff[idx] & (1 << pos))//若当前位为1,则访问右子树,为0,则访问左子树pCur = pCur->_pRight;elsepCur = pCur->_pLeft;if (pCur&&pCur->_pLeft == NULL&&pCur->_pRight == NULL)//当前节点为叶子节点{wBuff[wSize++] = pCur->_weight._ch;//将当前字符写入buffif (wSize == 1024)//若buff满1024,写入文件{fwrite(wBuff, 1, wSize, fIn);wSize = 0;}if (0 == --FileSize)//若buff不满1024,但文件已读完,将其直接写入文件{fwrite(wBuff, 1, wSize, fIn);//此处大小一定为buff中内容的大小,否则会出现随机值,第三个参数的类型为size_t,int无法写入break;}pCur = pRoot;//每找到一个叶子节点,便将当前节点指向根节点,否则会崩溃}if (pos == 0)//若pos为8,置0,重新计数{pos = 8;break;}}}}//关闭文件fclose(fOut);fclose(fIn);}private://获取哈弗曼编码void _GetHuffManCode(HuffmanTreeNode<FileInfo>* pRoot){if (pRoot){_GetHuffManCode(pRoot->_pLeft);_GetHuffManCode(pRoot->_pRight);if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//叶子节点{HuffmanTreeNode<FileInfo>* pCur = pRoot;HuffmanTreeNode<FileInfo>* pParent = pCur->_pParent;std::string strCode;while (pParent){if (pParent->_pLeft == pCur)//若当前节点是双亲结点的左孩子,编码为0{strCode += '0';}if (pParent->_pRight == pCur)//若当前节点是双亲结点的左孩子,编码为1{strCode += '1';}pParent = pParent->_pParent;pCur = pCur->_pParent;}std::reverse(strCode.begin(), strCode.end());//由于上边获取编码是从叶子节点开始的,所以拿到编码后需要逆置_fileInfo[pRoot->_weight._ch]._strCode = strCode;//将编码保存到结构体内}}}std::string GetFileName(const std::string strFilePath)//获取文件名{//com.txt//E://code//com.txtsize_t  last = strFilePath.find_last_of("/");//找到租后一个‘/’size_t first = strFilePath.find_first_of(".");//找到第一个‘.’std::string fileName = strFilePath.substr(last + 1, first);//获取中间的子串,即文件名return fileName;}std::string GetPostFix(const std::string strFilePath)//获取文件后缀{//com.txt//E:\code\com.txtsize_t first = strFilePath.find_last_of(".");//找到读后一个‘.’size_t end = strFilePath.size() - 1;//字符串的结尾std::string fileName = strFilePath.substr(first, end);//获得子串,即文件后缀return fileName;}std::string ReadLine(FILE* fp)//读取文件的每一行内容{std::string strLine;if (feof(fp))//检查文件是否为空return strLine;unsigned char c = fgetc(fp);while (c != '\n'){strLine += c;if (feof(fp))return strLine;c = fgetc(fp);}return strLine;}
private:FileInfo _fileInfo[256];};

结果如图:

哈夫曼树实现文件的压缩与解压缩相关推荐

  1. 用赫夫曼树进行文件的压缩

    思路分析 代码实现 package com.atguigu.huffmancode;import com.sun.org.glassfish.external.statistics.CountStat ...

  2. 基于哈夫曼编码对文件进行压缩和解压缩(详细讲解)

    基于哈夫曼编码对文件进行压缩和解压缩(详细讲解) 本文对应c++代码实现链接 一.背景 利用特定的算法来压缩数据的工具,压缩后生成的文件称为压缩包.如果想使用其中的数据,就得用压缩软件对数据进行解压. ...

  3. 哈夫曼树实现文件压缩

    最近在学了哈夫曼树之后,作为练习,写了一个文件压缩的小项目: 在这里和大家分享一下: 主要实现思路: 利用哈夫曼树的特性对字符进行哈夫曼编码,其中运用到了最小堆:利用最小堆的特性,找出构造哈夫曼树的结 ...

  4. 用赫夫曼树进行文件解压

    思路分析 代码实现 package com.atguigu.huffmancode;import com.sun.org.glassfish.external.statistics.CountStat ...

  5. 哈夫曼编码实现文件的压缩和解压

    哈夫曼编码的概念 哈夫曼编码是基于哈夫曼树实现的一种文件压缩方式. 哈夫曼树:一种带权路径最短的最优二叉树,每个叶子结点都有它的权值,离根节点越近,权值越小(根节点权值为0,往下随深度增加依次加一), ...

  6. Python+Qt 使用哈夫曼编码对文本文件进行压缩,解压缩

    from Huffman_Ui import * from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui i ...

  7. 哈夫曼树实现压缩文件

    哈夫曼树实现文件的压缩 #include <stdio.h> #include <stdlib.h> #include <string.h>//以字节形式读入目标文 ...

  8. 基于哈夫曼算法的文件压缩软件

    数据结构课设(一) 作业要求 1.设计并实现一个使用哈夫曼算法对文件进行压缩的工具软件. 2.通过命令行参数指定操作模式(压缩/解压).源文件名.目标文件名. 3.压缩操作将源文件按字节读入并统计字节 ...

  9. 【赫夫曼树详解】赫夫曼树简介及java代码实现-数据结构07

    赫夫曼树(最优二叉树) 1. 简介 定义: 赫夫曼树是n个带权叶子结点构成的所有二叉树中,带权路径长度(WPL)最小的二叉树. 叶子结点的带权路径: 叶子结点权值*到根节点的路径长度(叶结点的层数) ...

最新文章

  1. 代码坏味道之非必要的
  2. shell批量监控网站状态码
  3. Keras MNIST
  4. Refusing to install package with name “vue-i18n“ under a package
  5. python 条件概率_使用Pymc3的条件概率
  6. 二元函数洛必达求极限_洛必达法则的几个例子
  7. 大数据预测实战-随机森林预测实战(四)-模型调参
  8. 集合框架(List、Collection、迭代器)
  9. vs资源视图加载失败
  10. 2019年第四次课程设计实验报告
  11. BZOJ3707 圈地
  12. android 360 悬浮窗,悬浮窗的实现(如360悬浮窗效果)
  13. Taskctl的定时任务调度
  14. [SAP ABAP开发技术总结]SD销售订单定价过程
  15. 【vue3】ref获取v-for循环渲染的元素
  16. maven项目-加载不到spring文件,BeanFactory not initialized or already closed - call 'refresh' bef
  17. unity实现简单游戏——井字棋
  18. Photoshop从入门到发疯(一)身份证添加水印
  19. 华为面试题: 杨辉三角形的变形
  20. 什么是高铁运营监测(转载)

热门文章

  1. 心电监护仪数据图解_心电监护仪原理和使用步骤详解
  2. 神级操作丨用 Python 将微信热文转换成Word文档
  3. 深入浅出自然语义处理原理并构建自然语义处理(NLP)模型GPT2
  4. Global Illumination_Screen-Space Directional Occlusion(SSDO)
  5. 【c51单片机】交通红绿灯设计
  6. Python下载网易云音乐(云音乐飙升榜)
  7. 关于p操作和v操作的理解
  8. 解决CSDN导入md文档时图片显示不出来,出现[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jiakLQhy-1628602853830)提示
  9. QT写word的三种方式
  10. 抱歉,我也不知道程序员35岁以后该怎么办!