使用赫夫曼编码对字符串和文件进行压缩和解压缩(无损压缩)
操作步骤:
压缩:
1.先统计每一个byte或文件中字节出现的次数,并放入一个集合中
2.创建一颗赫夫曼树(根据字节 和他的权值也就是出现的次数创建的)
3.创建一个赫夫曼编码表(一个·字节:字节路径 的Map)
4.编码(将每个字节路径编码为一个byte数组)【如果是文件保存文件和赫夫曼表】
解压缩:
1.根据赫夫曼编码表key value 转换(如果是文件 要读取文件还要读取赫夫曼表)
2.根据value 获取key也就是字节
3.转换为字节数组(如果是文件将转换后的文件保存即可)
注:运用赫夫曼压缩的文件后缀可以是任意形式
package com.zhao.algorithm.tree; import java.io.*; import java.util.*; /** * AUTHOR :zhao * 日期:2020/2/17 17:42 * 赫夫曼编码 压缩与解压缩, 他是无损压缩 */ public class HuffmanCode { public static void main(String[] args) { // start 赫夫曼编码 解码 String msg="Man proposes,God disposes."; byte[] bytes = msg.getBytes(); System.out.println("压缩前长度 :"+bytes.length); //进行赫夫曼编码压缩 byte[] b = huffmanZip(bytes); System.out.println("压缩后长度 :"+b.length); //使用赫夫曼编码进行解码 byte[] newBytes = decode(huffCodes,b); System.out.println(new String(newBytes)); // end --------- // //user.dir指定了当前的路径 // String src=System.getProperty("user.dir")+"\\haffman.bmp"; // //这里的压缩名后缀随便起 // String dst="haffman.god"; // try { // zipFile(src, dst); // } catch (IOException e) { // e.printStackTrace(); // } // try { // unZip(dst, "haffman2.bmp"); // } catch (Exception e) { // e.printStackTrace(); // } } /** * 文件的解压 * @param src * @param dst * @throws Exception */ public static void unZip(String src,String dst) throws Exception { //创建一个输入流 InputStream is = new FileInputStream(src); ObjectInputStream ois = new ObjectInputStream(is); //读取byte数组 byte[] b = (byte[]) ois.readObject(); //读取赫夫曼编码表 Map<Byte, String> codes = (Map<Byte, String>) ois.readObject(); ois.close(); is.close(); //解码 byte[] bytes = decode(codes, b); //创建一个输出流 OutputStream os = new FileOutputStream(dst); //写出数据 os.write(bytes); os.close(); } /** * 压缩文件 * @param src 源文件 * @param dst 压缩后的文件 * @throws IOException */ public static void zipFile(String src,String dst) throws IOException { //创建一个输入流 InputStream is = new FileInputStream(src); //创建一个和输入流指向的文件大小一样的byte数组 // available 返回从此输入流中可以读取(或跳过)的剩余字节数的估计值,而不会被下一次调用此输入流的方法阻塞。 byte[] b = new byte[is.available()]; //读取文件内容 is.read(b); is.close(); //使用赫夫曼编码进行编码 byte[] byteZip = huffmanZip(b); //输出流 OutputStream os = new FileOutputStream(dst); ObjectOutputStream oos = new ObjectOutputStream(os); //把压缩后的byte数组写入文件 oos.writeObject(byteZip); //把赫夫曼编码表写入文件 oos.writeObject(huffCodes); oos.close(); os.close(); } /** * 使用指定的赫夫曼编码表进行解码 * * @param huffCodes * @param b * @return */ private static byte[] decode(Map<Byte, String> huffCodes, byte[] bytes) { StringBuilder sb = new StringBuilder(); //把byte数组转为一个二进制的字符串 for (int i = 0; i < bytes.length; i++) { byte b = bytes[i]; //是否是最后一个。 boolean flag = (i == bytes.length - 1); sb.append(byteToBitStr(!flag, b)); } //把字符串按照指定的赫夫曼编码进行解码 //把赫夫曼编码的键值对进行调换 Map<String, Byte> map = new HashMap<>(); for (Map.Entry<Byte, String> entry : huffCodes.entrySet()) { map.put(entry.getValue(), entry.getKey()); } //创建一个集合,用于存byte List<Byte> list = new ArrayList<>(); //处理字符串 for (int i = 0; i < sb.length(); ) { int count = 1; boolean flag = true; Byte b = null; //截取出一个byte while (flag) { String key = sb.substring(i, i + count); b = map.get(key); if (b == null) { count++; } else { flag = false; } } list.add(b); i += count; } //把集合转为数组 byte[] b = new byte[list.size()]; for (int i = 0; i < b.length; i++) { b[i] = list.get(i); } return b; } private static String byteToBitStr(boolean flag, byte b) { int temp = b; if (flag) { temp |= 256; } String str = Integer.toBinaryString(temp); if (flag) { return str.substring(str.length() - 8); } else { return str; } } /** * 进行赫夫曼编码压缩的方法 * * @param bytes * @return */ private static byte[] huffmanZip(byte[] bytes) { //先统计每一个byte出现的次数,并放入一个集合中 List<HuffmanNode> nodes = getNodes(bytes); //创建一颗赫夫曼树 HuffmanNode tree = createHuffmanTree(nodes); //创建一个赫夫曼编码表 Map<Byte, String> huffCodes = getCodes(tree); //编码 byte[] b = zip(bytes, huffCodes); return b; } /** * 把byte数组转为node集合 * * @param bytes * @return */ private static List<HuffmanNode> getNodes(byte[] bytes) { List<HuffmanNode> nodes = new ArrayList<>(); //存储每一个byte出现了多少次。 Map<Byte, Integer> counts = new HashMap<>(); //统计每一个byte出现的次数 for (byte b : bytes) { Integer count = counts.get(b); if (count == null) { counts.put(b, 1); } else { counts.put(b, count + 1); } } //把每一个键值对转为一个node对象 for (Map.Entry<Byte, Integer> entry : counts.entrySet()) { nodes.add(new HuffmanNode(entry.getKey(), entry.getValue())); } return nodes; } /** * 进行赫夫曼编码 * * @param bytes * @param huffCodes * @return */ private static byte[] zip(byte[] bytes, Map<Byte, String> huffCodes) { StringBuilder sb = new StringBuilder(); //把需要压缩的byte数组处理成一个二进制的字符串,其实就是他每个字母的赫夫曼路径 for (byte b : bytes) { sb.append(huffCodes.get(b)); } //定义长度 int len; if (sb.length() % 8 == 0) { len = sb.length() / 8; } else { len = sb.length() / 8 + 1; } //用于存储压缩后的byte byte[] by = new byte[len]; //记录新byte的位置 int index = 0; for (int i = 0; i < sb.length(); i += 8) { String strByte; if (i + 8 > sb.length()) { strByte = sb.substring(i); } else { strByte = sb.substring(i, i + 8); } byte byt = (byte) Integer.parseInt(strByte, 2); by[index] = byt; index++; } return by; } //用于临时存储路径 static StringBuilder sb = new StringBuilder(); //用于存储赫夫曼编码 static Map<Byte, String> huffCodes = new HashMap<>(); /** * 根据赫夫曼树获取赫夫曼编码 * * @param tree * @return */ private static Map<Byte, String> getCodes(HuffmanNode tree) { if (tree == null) { return null; } getCodes(tree.left, "0", sb); getCodes(tree.right, "1", sb); return huffCodes; } /** * 获取赫夫曼 编码中的 数据 eg:b 和数据出现的路径 huffCodes * * @param node 树节点 * @param code 0 左节点 1右节点 * @param sb 获取这个节点中的数据要走的路径 */ private static void getCodes(HuffmanNode node, String code, StringBuilder sb) { StringBuilder sb2 = new StringBuilder(sb); sb2.append(code); if (node.data == null) { getCodes(node.left, "0", sb2); getCodes(node.right, "1", sb2); } else { huffCodes.put(node.data, sb2.toString()); } } /** * 创建赫夫曼树 * * @param nodes * @return */ private static HuffmanNode createHuffmanTree(List<HuffmanNode> nodes) { while (nodes.size() > 1) { //排序 Collections.sort(nodes); //取出两个权值最低的二叉树 HuffmanNode left = nodes.get(nodes.size() - 1); HuffmanNode right = nodes.get(nodes.size() - 2); //创建一颗新的二叉树 HuffmanNode parent = new HuffmanNode(null, left.weight + right.weight); //把之前取出来的两颗二叉树设置为新创建的二叉树的子树 parent.left = left; parent.right = right; //把前面取出来的两颗二叉树删除 nodes.remove(left); nodes.remove(right); //把新创建的二叉树放入集合中 nodes.add(parent); } return nodes.get(0); } }
package com.zhao.algorithm.tree; /** * AUTHOR :zhao * 日期:2020/2/17 17:39 * 用于赫夫曼编码的树节点 */ public class HuffmanNode implements Comparable<HuffmanNode> { /**数据*/ Byte data; /**数据出现的次数*/ int weight; /**当前节点的左节点*/ HuffmanNode left; /**当前节点的右节点*/ HuffmanNode right; public HuffmanNode(Byte data,int weight) { this.data=data; this.weight=weight; } @Override public String toString() { return "HuffmanNode [data=" + data + ", weight=" + weight + "]"; } @Override public int compareTo(HuffmanNode o) { return o.weight-this.weight; } }

浙公网安备 33010602011771号