Huffman文件压缩

Huffman文件压缩是一种经典的无损压缩方式,刚做完Huffman图片压缩实验不久,写一篇文章总结一下。所用环境为VS2013.

原理

1、所有文件都可以转化为一个一个的字节,例如Java中有字节流,就是通过字节的方式读写文件,C++中也是如此,这也就提供了统计文件中字节权重的方式。
2、可以通过统计到的字节的权重构建Huffman树,从而得到Huffman编码。
3、每个字节是八位二进制,而得到的Huffman编码长度可能小于八位(这时的编码是字符串,可以用Huffman编码替代字符,然后对编码进行位操作,每八位编码转换为一个字符,这样就压缩了空间。
举个例子:
有如下内容的文本:
aaaaaaabbbbbccdddd
a有7个,b有5个,c有2个,d有4个,通过Huffman编码可以得到如下编码:
a 0
b 10
c 110
d 111
用编码代替原文件中的内容:
00000001010101010110110111111111111
长度为35,没八位转换为一个字符,可以得到5个字符(不够补0),而原文有18个字符,这样就达到了压缩的目的。

实现

原理理解起来应该不太难,但要写起来需要注意到的地方挺多的。
首先就是如何统计字符的权重:

void CountWeight(const char* pFilename)
{
    FILE *in = fopen(pFilename, "rb"); // rb是以二进制的方式打开
    unsigned char ch = fgetc(in);

    while (!feof(in))
    {
        weight[ch]++;
        count1++;
        ch = fgetc(in);
    }
    fclose(in);
}

注意字符的类型为unsigned char,还要注意,不要在while循环中输出读到的东西,如果文件有几百kb的话,你会怀疑程序是不是停不下来,然后各种换IO流,不要问我为什么知道。。。

接下来就是建立Huffman树了:

void Huffman(HuffmanTree& HT, int weight[], int n)
{
    if (n < 1)
        return;

    int m = 2 * n - 1; // 结点个数

    HT = (HuffmanTree)malloc((m + 1)*sizeof(HTNode));
    int i;
    HT[0].weight = 0;
    HT[0].lchild = 0;
    HT[0].rchild = 0;
    HT[0].parent = 0;
    for (i = 1; i <= n; i++) // 初始化叶子结点
    {
        HT[i].weight = weight[i-1];
        HT[i].lchild = 0;
        HT[i].rchild = 0;
        HT[i].parent = 0;
    }

    for (i = n+1; i <= m; i++) // 初始化父结点
    {
        HT[i].weight = 0;
        HT[i].lchild = 0;
        HT[i].rchild = 0;
        HT[i].parent = 0;
    }

    for (i = n + 1; i <= m; ++i){ // 建树
        int s1, s2;
        Select(HT, s1,s2,i-1);
        HT[s1].parent = i;
        HT[s2].parent = i;
        HT[i].lchild = s1;
        HT[i].rchild = s2;
        HT[i].weight = HT[s1].weight + HT[s2].weight;
    }

}

然后是编码:

void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int n) 
{
    HC = (HuffmanCode)malloc((n + 1)*sizeof(char*));
    char *cd = (char*)malloc(n*sizeof(char));
    cd[n - 1] = '\0';
    for (int i = 1; i <= n; ++i){
        int start = n - 1;
        for (int c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent){
            if (HT[f].lchild == c){
                cd[--start] = '0';
            }
            else {
                cd[--start] = '1';
            }
        }
        HC[i] = (char*)malloc((n - start) * sizeof(char));
        strcpy(HC[i], &cd[start]);
    }
}

这些都做完后就能得到Huffman编码了,然后用编码表示文件中的字符,用一个字符串来存储,这个字符串很长很长。
然后就是进行位操作了:

result_out.open(s, std::ios::out | std::ios::app); // 压缩后的文件
    int t = 0;
    for (int i = 0; i < pBinStr.length(); i++)
    {
        char b = 0x00;
        for (int j = 0; j < 8 && i < pBinStr.length(); j++,i++)
        {
            b = b << 1;
            if (pBinStr.at(i) == '1')
                b = b | 0x01;
        }
        i--;
        result_out << (char)b;
        t++;
    }

然后就可以看看压缩效果了,测试的时候需要注意,并不是所有的图片压缩效果都很好,bmp格式的图片压缩效果应该是比较好的,如果不好,用画图工具打开,将图片另存为256位图,或者找一张色彩单调的图片。如果是其他格式的文件,压缩效果几乎为0,甚至会变大一点点,这都是正常现象,原因在于这些文件中每个字符的权重差别不大,导致编码长度都为8左右,导致压缩效果很不好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值