诸位既然点开了本帖,相信对此问题已有初步了解,哈夫曼树的原理不再赘述,我们开门见山,直入主题。
一、概要设计
问题拆解:设计一个基于哈夫曼编码的解压缩软件,这个问题我认为可以分解为以下几个子问题:
- 读取传入文件,进行字符权重统计
- 将出现的字符放入哈夫曼树结点,构建哈夫曼树,获取哈夫曼编码
- 将编码相关信息写入压缩后的文件,再将传入文件的每个字符按照哈夫曼编码转换,每8个二进制位作为一个字节传入压缩后的文件
- 解压部分:将传入的已压缩文件进行文件流读取,获取编码信息进行还原
根据这几个子问题的思路顺序,我们逐个击破,寻找解决方案:
- 读取传入文件,需要用到文件流。这里推荐使用Qt自带的
QFile文件流配以QDatastram辅助。因为,C++的文件流fstream无法识别中文路径(一说起这个,就想起Debug时的辛酸)。权重统计我用的是map记录:map<unsigned char ,int>Weightmap。 - 哈夫曼结点的构造,和二叉树没有什么区别,只是在封装的
struct里面加了unsigned char型的字符,int型的权重,string型的哈夫曼编码,和是否是叶子结点的bool型标记(哈夫曼树叶子结点才是我们需要的编码)。将前面我们获取到的字符和权重加进去。
我们将建立好的结点放入到一个vector内(理论上什么容器都可以),进入循环:根据哈夫曼结点的权重比较进行排序(直接调用sort),每次将连个最小的结点权重取出,相加得到权重和,以权重和建立一个新的结点,新结点的左右孩子结点就是这两个结点,将新结点加入到vector中,那两个结点删除。循环结束条件为vector内只剩下一个结点。这个结点便是哈夫曼树的根结点,保存一根结点足矣。
我们从根结点出发遍历,左子树的string+“0”,右子树的string+“1”。并将unsigned char型的字符与这个string用map<unsigned char,string> PasswordMap记录。 - 传入辅助信息阶段:我们传入的信息有,
PasswordMap.size(),循环传入PasswordMap->first(字符),PasswordMap->second.size()(记录编码长度),PasswordMap->second(这个字符串有可能超过8位,每个字节不足8位的部分补0)直到完全传入。另外一个方法是将所有字符与字符权重传入,在解压缩时再次构建哈夫曼树,两种思路我觉得都可以。这里我采用的第一种方法。
传入哈夫曼编码阶段:遍历先前在读取文件时获取的字符串,将字符串的每一位转化为哈夫曼编码,用另一个字符串储存。这里不妨称这个字符串为二进制字符串,这个二进制字符串按8分割到最后可能会有不足8位的部分,对其进行补0操作。 最后再传入一个字节,记录补0数。 - 解压部分:对应上部分的操作进行对应读取,主要思路是将压缩时传入的辅助信息提取,建立一个新的
Map映射,根据这个映射,将原文件还原。
二:设计效果展示:
压缩前:

压缩中:

压缩后:

解压中:

解压后:

三、源代码
前面分析得很清晰,不过写代码的过程异常艰辛,充满了Debug时的汗水和泪水。
完整代码与封装好的可执行文件下载链接:https://gitee.com/sherlocknovitch/Qt_Compression
(可执行文件有58M,是因为封装了Qt的核心控件,去掉这些估计只有几百Kb的大小)
上代码:
1°MainWindow.h
先从窗口头文件看起,这块没有什么实质性的内容,只有几个槽函数(3个按钮的Click槽函数加一个让QprogressBar动起来的槽函数和一个出现文件打开错误情况时让QLineEdit清空的槽函数)
私有成员中QStirng path的作用是读取文件路径,作为压缩函数和解压缩函数的参数。
Compression* com是一个类指针,这个类是压缩类,在后面会提及。
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include"compression.h"
#include<QFileDialog>
#include<QMenu>
#include<QAction>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void clear();
private slots:
void on_pushButton_open_clicked();
void on_pushButton_compression_clicked();
void on_pushButton_decompression_clicked();
void myslot(double per);
private:
Ui::MainWindow *ui;
Compression* com;
QString path;
};
#endif // MAINWINDOW_H
2°MainWindow.cpp
这一块主要含义见注释即可,QProgressBar是Qt的一个控件,可以理解为进度条
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
resize(600,600);
//设置窗口大小
setWindowTitle("Sherlock's Compression");
/设置窗口名称
com=new Compression();
//初始化压缩类指针
connect(com,&Compression::error,this,&MainWindow::clear);
//异常情况的信号接收
connect(com,SIGNAL(mysignal(double)),this,SLOT(myslot(double)));

本文详细介绍了使用Qt实现哈夫曼编码的解压缩软件的设计过程,包括概要设计、设计效果展示、源代码分析及软件分析。通过字符权重统计构建哈夫曼树,进行文件的压缩和解压缩操作。文章提供了完整代码和执行文件的下载链接,并讨论了不同文件类型的压缩效率。
最低0.47元/天 解锁文章
1万+





