PHP实现Huffman编码/解码的示例代码
Huffman 编码是一种数据压缩算法。我们常用的 zip 压缩,其核心就是 Huffman 编码,还有在 HTTP/2 中,Huffman 编码被用于 HTTP 头部的压缩。 本文就来用 PHP 来实践一下 Huffman 编码和解码。 1. 编码字数统计Huffman编码的第一步就是要统计文档中每个字符出现的次数,PHP的内置函数 count_chars() 就可以做到: 构造Huffman树接下来根据统计结果构造Huffman树,构造方法在 Wikipedia 有详细的描述。这里用PHP写了一个简易版的: $count) { $huffmanTree[] = [ 'k' => chr($char),'v' => $count,'left' => null,'right' => null,]; }// 构造树的层级关系,思想见wiki:https://zh.wikipedia.org/wiki/%E9%9C%8D%E5%A4%AB%E6%9B%BC%E7%BC%96%E7%A0%81 经过计算之后,$root 就会指向 Huffman 树的根节点 根据Huffman树生成编码字典有了 Huffman 树,就可以生成用于编码的字典: 写文件运用字典将文件内容进行编码,并写入文件。将Huffman编码写入文件的有几个注意的地方: 将编码字典和编码内容一起写入文件后,就没法区分他们的边界了,因此需要在文件开始写入他们各自占用的字节数 PHP提供的 fwrite() 函数一次能写入 8-bit(一个字节)或者是 8的整数倍个bit。但Huffman编码中,一个字符可能只使用 1-bit 表示,PHP不支持只往文件中写入 1-bit 这种操作。所以需要我们自行对编码进行拼接,每凑齐 8-bit 才写入文件。
与第二条类似,最终形成的文件大小一定是 8-bit 的整数倍。所以如果整个编码的大小是 8001-bit的话,还要在末尾补上 7个 0 // 写入编码的内容$buffer = ''; $i = 0; while (isset($input[$i])) { $buffer .= $dict[$input[$i]]; while (isset($buffer[7])) { $char = bindec(substr($buffer,8)); fwrite($outFile,chr($char)); $buffer = substr($buffer,8); } $i++; } // 末尾的内容如果没有凑齐 8-bit,需要自行补齐 if (!empty($buffer)) { $char = bindec(str_pad($buffer,8,'0')); fwrite($outFile,chr($char)); } fclose($outFile); 解码Huffman编码的解码相对简单:先读取编码字典,然后根据字典解码出原始字符。 解码过程有个问题需要注意:由于我们在编码过程中,在文件末尾补齐了几个0-bit,如果这些 0-bit 在字典中恰巧是某个字符的编码时,就会造成错误的解码。 所以解码过程中,当已解码的字符数达到文档长度时,就要停止解码。 // 读出字典长度和编码内容长度 $header = unpack('VdictLen/VcontentLen',$content); $dict = unserialize(substr($content,$header['dictLen'])); $dict = array_flip($dict); $bin = substr($content,8 + $header['dictLen']); 试验我们将Huffman编码Wiki页 的HTML代码保存到本地,进行Huffman编码测试,试验结果:
空间节省了 33%,如果原文的重复内容较多,Huffman编码节省的空间可以达到 50% 以上. 除了文本内容,我们再尝试将一个二进制文件进行Huffman编码,比如 f.lux的安装程序 ,试验结果如下:
编码后反而占用了更大的空间,一方面是由于我们存储字典时,并没有做额外的处理,占用了不少空间。另一方面,二进制文件中,各个字符出现的概率相对比较平均,无法发挥Huffman编码的优势。 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。 (编辑:好传媒网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |