huffman编码、压缩,曾经写过一次,感觉很痛苦;最近,学习数据结构,又试了一次,感觉好多了。这里写下,总结下。
huffman编码、压缩,我觉得主要两件事情:1、对字符生成对应的huffman编码;2、对数据进行huffman压缩。这些书本、网络介绍的太多了(比如:http://coolshell.cn/articles/7459.html),而且也很容易理解,我这里简单的讲下:将要压缩的数据进行遍历、统计;根据统计结果创建huffman树;根据huffman树,得到每个字符的huffman编码。
对数据进行huffman压缩。有了huffman编码,那么再遍历一遍要压缩的数据,那么一一对应到每一个编码,那么就可以得到每一字符的huffman编码,写文件就可以了,完成huffman压缩了。
但是,这里面有一个很重要的问题:二进制读写。这里,我主要讨论二进制操作问题。
huffman的每一个编码不是等长的,那么,怎么将这些数据对应的编码连接起来,做成完整的数据呢?这里,我想到一个比较不错的想法,跟大家分享下。
1、在编码的时候,用每一个字符对应的huffman编码,不要用二进制表示,而是字符串表示。比如,假设‘a’对应的编码是‘010’,用的是3个字节+一个‘\0';而非3bit。
2、在对原始数据编码的时候,很容易的,直接就是字符串的拼接;
3、对应的编码之后,要将01字符串转成二进制,可以8字节为单位,进制转码;要对一个字节的某个bit位置1还是很容易的(0的话不用操作)。(我这里的话,用的是1024个01字符串作为1组,那么正好转为128个字节的bit;而这个,正好是fd_set这个类型的长度;那么,对bit位的操作,可以调用FD_XXX宏了,比如清零是FD_ZERO,置一是FD_SET;某bit位是否是1可以用FD_TEST)
4、这样,最终需要长度控制的地方,就是最后的一部分。(所以,在huffman编码的文件,可能还需要记录下最后字节的bit长度)
当然,在解压缩的时候,需要huffman树,那么在压缩的时候需要保存(我这里想到的是保存对字符数量的统计,然后解压的时候重新建立huffman树)。那么,从bit到字符,也是需要访问huffman树,这里就需要知道对应的bit位是0还是1,这个也很方便(比如我刚刚说的,FD_TEST就可以做到)。
至此,huffman应该就没多大的问题了。
附:关于huffman编码、压缩,可能只适用于文字压缩;或者,和其他的类型的压缩算法配合。
另外,还有一个小问题:这类压缩文件,一般都会需要将某些长度保存到压缩文件中去,比如文件长度。这类多字节的整形(比如int),最好的话,还是转为网络字节序保存,这是为了卡平台(可能我想多了 吧^_^)
最后,关于bit位置值,这里给个另外一种,不借助fd的(可能有的编译环境上没有网络库)。
背景条件:对文件的huffman编码之后的'0','1'字符串(以8字节为单位长度);将这类字符串转化为对应的bit。注意:这里没有“善后”:即在最后的时候,可能‘0’,‘1’字符串不满8字节。
char nbit[8]={0x01, //第一个bit位为1;
0x02, //第二个bit位为1;
0x04, //第三个bit位为1;
0x08,
0x10,
0x20,
0x40, //第七个bit位为1;
0x80} //第八个bit位为1;
void setNbit(char *bitBuf,char *data,size_t len)
{
size_t bitPos = 0;
for (size_t i=0; i!=len; ++i)
{
bitPos = i % 8;
char &c = *bitBuf;
c = c | nbit[bitPos];
if (bitPos == 7)
{
++bitBuf;
}
}
}
附注:由于huffman编码可能会出现极度不平衡的情况,比如:跟节点+左子树+右子树(叶子节点),如此反复;那么,不平衡程度极高,会导致某些节点的编码是很长的,而你的一个节点的编码长度是有限制的,那么这就是个问题。
这个问题我碰到过,当初是对图像进行编码、压缩。原始数据是像素的,一个字节可以表示;但是huffman编码之后,许多是超过16bit的。
如果,你想对编码长度进行限制,也许有一种方案可行:平衡二叉树的思想,进行左转、右转。
我没试过;但是,有空的时候,打算去试试。