huffman编码压缩与解码bug处理

    技术2025-10-24  12

    huffman编码压缩与解压

    学习huffman编码后,了解了网络上主流的编码压缩和解码方式。但解压代码中存在一些bug,在这里和大家分享一下解决方法。


    bug如下:

    解压时,最后一个字节转成二进制时,位数的不确定性可能导致解压后字符串错误。比如,byte 存储为1,原先的编码后的字符串最后几位可能为 001 或者 0001。使用Integer中的字节转换二进制,解析byte1,正数只会返回1,此时如果不做处理,解码后的字符串可能会少0,可能报异常或者解压错误

    解决方法:

    思路:单独处理最后一个byte,还是按照原先的方式先转化成二进制字符串,然后与原先编码获得的字符串比较长度,如果相等,那就直接拼接,如果不相等,那就先末端补0,同时比较,直到解压后的字符串长度和编码后的字符串长度一致。处理bug部分代码如下: // 首先转化成二进制符号,然后拼接成字符串-注意末尾byte的处理 StringBuilder str = new StringBuilder(); boolean flag; byte b; for (int i = 0; i < huffmanBytes.length - 1; i++) { // 判断是否被8整除,如果整除就不用判断最后一个数据,全部需要补位截取 b = huffmanBytes[i]; str.append(byteToBinary(true, b)); } // 单独处理一下最后一位byte b = huffmanBytes[huffmanBytes.length - 1]; String lastByteStr = byteToBinary(false, b); // 如果长度相等,那正好。拼接上去 if (str.length() + lastByteStr.length() == huffmanCodesStr.length()) { str.append(lastByteStr); } else { // 如果长度不够,那就先补0,直到总长度相等,再拼接 while (str.length() + lastByteStr.length() < huffmanCodesStr.length()) { str.append(0); } str.append(lastByteStr); }

    完整代码供大家参考:

    解压及byte转化成二进制代码如下:

    // 解压 public static byte[] decode(byte[] huffmanBytes, Map<Byte, String> pathCodes) { // 首先转化成二进制符号,然后拼接成字符串-注意末尾byte的处理 StringBuilder str = new StringBuilder(); boolean flag; byte b; for (int i = 0; i < huffmanBytes.length - 1; i++) { // 判断是否被8整除,如果整除就不用判断最后一个数据,全部需要补位截取 b = huffmanBytes[i]; str.append(byteToBinary(true, b)); } // 单独处理一下最后一位byte b = huffmanBytes[huffmanBytes.length - 1]; String lastByteStr = byteToBinary(false, b); // 如果长度相等,那正好。拼接上去 if (str.length() + lastByteStr.length() == huffmanCodesStr.length()) { str.append(lastByteStr); } else { // 如果长度不够,那就先补0,直到总长度相等,再拼接 while (str.length() + lastByteStr.length() < huffmanCodesStr.length()) { str.append(0); } str.append(lastByteStr); } System.out.println("解码后的哈夫曼编码字符串为:" + str.toString() + "长度为:"+ str.length()); // 然后对应原先的map表 i=100 反转 制定新的map表 100 =i Map<String, Byte> map = new HashMap<String, Byte>(); for (Map.Entry<Byte, String> entry : pathCodes.entrySet()) { map.put(entry.getValue(), entry.getKey()); } // 扫描上面的code字符串,对应出单词空格保存到list中 List<Byte> list = new ArrayList<>(); for (int j = 0; j < str.length();) { // i只是前进所用,真正扫描寻找到有为止,还需要一个索引 int count = 1; // 遍历查找的结束标识 boolean flag2 = true; String subStr = null; // 注意越界判断 while (flag2 && (j + count) <= str.length()) { // 截取子串,进行map中key值比较 subStr = str.substring(j, j + count); // b2 = map.get(subStr); if (map.containsKey(subStr)) { flag2 = false; } else { // 没有的话继续遍历后移 count++; } } // 将这个byte添加到list中 list.add(map.get(subStr)); // 查找到一个后,后移到下一个j起始处.此处后移了j那么for中就不要再后移了 j += count; } // 扫描完所有的字符串,将list中的数据转化成byte[] byte[] bt = new byte[list.size()]; for (int i = 0; i < bt.length; i++) { bt[i] = list.get(i); } return bt; } // 首先需要一个将byte转化为电脑储存的补码二进制形式的方法 /** * @param flag * 传进来的字符串中是否末尾的标识 末尾不一定有8位长,直接补齐8位可能出错。所以不在这里补齐 比如 0100 00100 * 都是4 但是我们返回100即可,补齐的问题交给解压方法 * @param b * 需要转化的字节数 * @return */ public static String byteToBinary(boolean flag, byte b) { // Integer中有直接转化成二进制的方法 // 转化类型 int num = b; // 转化 // 正数转化呢,只有后几位,---所以需要补位0 补齐8 位 // 256 1 0000 0000 与256或计算,就可以得到后八位长度 负数不影响,只要后8位 if (flag) { num |= 256; } String binaryStr = Integer.toBinaryString(num); if (flag || num < 0) { // true 就是还没到末尾 都是8位 以及负数的情况 // 但是注意!负数转化完是int类型32位的二进制补码--所以需要取出后8位 return binaryStr.substring(binaryStr.length() - 8); } else { // false 到了末尾值,正数,不足8位,直接返回该有的长度就可以。 return binaryStr; } }

    完整的整个编码压缩过程请到这里: huffman编码实现数据压缩

    Processed: 0.009, SQL: 9