利用Huffman树实现文件压缩

项目名称

利用Huffman树实现文件压缩


项目概要

使用的编辑语言是C++,项目目的是能够实现对文件的压缩及解压,涉及到的技术主要有huffman树的实现,文件的IO操作,优先级队列等;

  • huffman算法原理: Huffman算法是一种无损压缩算法,Huffman需要得到每种字符出现概率,通过计算字符序列中每种字符出现的频率,为每种字符进行唯一的编码设计,使得频率高的字符占的位数短,而频率低的字符长,来达到压缩的目的。

  • 压缩原理:首先是处理源文件,统计源文件的字符数,将源文件以IO流的方式打开,每次从流中读取一个字符,进行处理,并将其记录到已有的字符哈希表中,由于一个字符占一个字节(0 ~ 255),因此哈希表的空间设置为256即可。完成统计之后,用哈希表来构建huffman数,生成huffman编码,根据01编码将每个字符以一个到多个比特位的方式来写入压缩文件即可。

  • 解压原理:在压缩文件中得到构建huffman树的所有数据(这里需要在压缩时将这些统计好的的数据同时写入压缩文件),根据这些数据将就可以将压缩时使用的huffman数还原,即可解析编码。之后读取压缩文件的所有编码,通过编码在huffman树上进行查找,得到原有的字符,并依次写入解压文件。


设计思路

整体思路

  1. 整个过程是依赖于huffman树,因此要构建出一个可供我们使用的Huffman树
  2. 压缩时,操作源文件,以字符形式读取文件信息,进行字符个数统计,将统计结果作为数据构建huffman树,生成huffman编码,进行压缩。
  3. 解压时,从压缩文件取出构建huffman树的数据,根据huffman树解析压缩文件。

具体实现

Huffman树的构建

  1. 将数据依次放入优先级队列中,构建成一个小堆。
  2. 每次从堆中拿出两个权值最小的数,进行操作,将操作后的权值再次放入堆中。
  3. 直到堆中的数据为空,就完成了huffman数的构造。

主要实现:

    //构建Huffman树需要三个参数,分别为:统计之后的数据数组,数据个数,非空元素
    HuffmanTree(T* data, size_t size, const T& end)
    {
        //使用优先级队列构建一个小堆   < 是大堆  > 是小堆
        priority_queue<Node*, vector<Node*>, compare> heap;

        //1.将所有数据构成结点,录入小堆中
        for (size_t i = 0; i < size; ++i)
        {
            if (data[i] != end)
                heap.push(new Node(data[i]));
        }

        //2. 开始构建HuffmanTree,每堆中取两个最小的数据,作为根节点的左子树和右子树,
        //   并将根节点录入堆中,直到堆中没数据
        Node* pN1;
        Node* pN2;
        Node* newNode=NULL;
        while (heap.size() > 1)
        {
            pN1 = heap.top();
            heap.pop();
            pN2 = heap.top();
            heap.pop();
            newNode = new Node(pN1->_data + pN2->_data);
            pN1->_parent = newNode;
            pN2->_parent = newNode;
            if (pN1->_data > pN2->_data)//出现频率高的放左边
            {
                newNode->_left  = pN1;
                newNode->_right = pN2;
            }
            else
            {
                newNode->_left = pN2;
                newNode->_right = pN1;
            }
            heap.push(newNode);
        }
        _root = newNode;
    }

压缩文件

文件压缩,首先统计源文件的所有字符,当完成字符统计之后,将数据传给huffman数的构造函数构造huffman树,之后便可以根据数进行生成huffman编码,将统计数据也要压入压缩文件,方便在进行解压之时,能够构造出同一棵huffman树。

在进行压缩时,从源文件读取一个字符,得到将字符的huffman编码,将编码以比特位的形式写入压缩文件,即够八位,进行一次写入。

主要实现:

//从压缩文件中读取一个字符,开始从HuffmanTree上开始查找,直到叶子结点,写入解压文件
    int ch = 0;
    int num = _root->_data._count;
    Node* cur = _root;
    while ( num > 0)
    {
        ch = fgetc(fp_r);
        for (int i = 0; i < 8; i++)
        {
            if ((ch & (1 << i)) ) //为1
                cur = cur->_left;
            else
                cur = cur->_right;
            if (cur && cur->_left == NULL && cur->_right == NULL)//找到叶子结点
            {
                --num;
                fputc((int)cur->_data._ch, fp_w);
                cur = _root;
                if (num == 0)
                    break;
            }
        }
    }

解压文件

解压时,向从压缩文件中读取统计数据,即:

扫描二维码关注公众号,回复: 2887326 查看本文章
//1.先从压缩文件中将要构成HuffmanTree的数据(字符和字符总数)取出
struct Data { char _ch; size_t _count; }data;
FILE* fp_r = fopen(file, "rb");
fread(&data, sizeof(data), 1, fp_r);
int num = 0;
while (data._count != 0)
{
    _hashtable[(unsigned char)data._ch]._count = data._count;
    fread(&data, sizeof(data), 1, fp_r);
    cout << num++ << endl;
}

之后,读取编码,开始从huffman树上解码,解码的过程是,从树的root开始,遇到1即走树的左子树,遇到0走树的右子树,直到走到叶子结点为止,将叶子结点上的字符,输入解压文件即可。

主要实现:

int ch = 0;
    int num = _root->_data._count;
    Node* cur = _root;
    while ( num > 0)
    {
        ch = fgetc(fp_r);
        for (int i = 0; i < 8; i++)
        {
            if ((ch & (1 << i)) ) //为1
                cur = cur->_left;
            else
                cur = cur->_right;
            if (cur && cur->_left == NULL && cur->_right == NULL)//找到叶子结点
            {
                --num;
                fputc((int)cur->_data._ch, fp_w);
                cur = _root;
                if (num == 0)
                    break;
            }
        }
    }

测试阶段:

经过多次的测试,虽然能够保证无损压缩,但是还是存在两个缺点:

  1. 压缩率不能保证,有些文件压缩之后比压缩之前的文件下不了多少。
  2. 速度慢,仅仅是4M左右的文件,也会耗费几秒,算法的处理还是不够。

如果大家有什么好的想法,可以想我提出。

项目源码地址https://github.com/zuo369301826/FileCompress-project

猜你喜欢

转载自blog.csdn.net/xiaozuo666/article/details/81109491