文件压缩与解压-霍夫曼编码
作者:互联网
1.背景
面试中问到霍夫曼编码,你知道么?
2.代码
package com.ldp.structure.demo06Zip; import org.junit.Test; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.*; /** * @create 05/01 5:06 * @description <p> * 需求:使用霍夫曼树实现压缩与解压功能 * 压缩 * 0.统计每个字符对应了个数,并以节点的形式存放,List<Node> * 如:d:1,表示d字母出现1次,使用节点存放为 Node{key=d,value=1} * a:5,表示a字母出现5次,使用节点存放为 Node{key=a,value=5} * <p> * 1.将上一步中的List<Node>转为霍夫曼树 * 2.将上一步中的霍夫曼树转变为霍夫曼编码表Map<bity,String> map * 如:d=11000,转为 map.put(d,"11000") * 如:a=100,转为 map.put(a,"100") * 3.使用上一步中的霍夫曼表,对"i like like like java do you like a java", * 逐个翻译,生成霍夫曼编码后的数据,即霍夫曼表对应的字符串,如:"101010001011...." * <p> * 4.对上一步中的霍夫曼编码"101010001011....",每8位转为一个bite,最后生成byte[]数组 * Integer.parseInt("10001100",2);表示传入一个二进制的字符串转为对应的byte * </p> */ public class Test01 { /** * 定义一个全局的霍夫曼编码 */ private Map<Byte, String> huoFuManCodeMap = new HashMap<>(); /** * 测试 * 字符串压缩与解压 */ @Test public void test01() { String str = "i like like like java do you like a java"; byte[] bytes2 = str.getBytes(); // 压缩 byte[] bytes = myZip(bytes2); // 解码 byte[] bytes1 = myUnZip(bytes, huoFuManCodeMap); System.out.println("解码后=>" + new String(bytes1)); } /** * 测试 * 文件压缩与解压 */ @Test public void test02() throws Exception { // 文件压缩 myFileZip("F:\\test\\t1.txt", "F:\\test\\t1.zip"); System.out.println("文件压缩完成..."); // 解压文件 myFileUnzip("F:\\test\\t1.zip", "F:\\test\\t2.txt"); System.out.println("文件解压完成..."); } /** * 文件解压 * * @param srcFileZipPath 需要解压的文件地址 * @param dstFilePath 解压后的文件地址 */ public void myFileUnzip(String srcFileZipPath, String dstFilePath) throws Exception { FileInputStream fileInputStream = new FileInputStream(srcFileZipPath); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); // 读取压缩后的字节数组 byte[] bytesZip = (byte[]) objectInputStream.readObject(); // 读取霍夫曼翻译码 Map<Byte, String> codes = (Map<Byte, String>) objectInputStream.readObject(); // 解压得到原文件字节数组 byte[] bytes = myUnZip(bytesZip, codes); // 输出 FileOutputStream fileOutputStream = new FileOutputStream(dstFilePath); fileOutputStream.write(bytes); } /** * 文件压缩 * * @param srcFilePath 原文件地址 * @param dstFilePath 压缩后的文件地址 */ public void myFileZip(String srcFilePath, String dstFilePath) throws Exception { // 创建文件输入流 FileInputStream fileInputStream = new FileInputStream(srcFilePath); // 创建与文件一样大小的字节数组 byte[] bytes = new byte[fileInputStream.available()]; // 读取文件到字节数组 fileInputStream.read(bytes); // 压缩文件,得到压缩后的文件字节数组 byte[] bytesZip = myZip(bytes); // 创建文件输出流 FileOutputStream fileOutputStream = new FileOutputStream(dstFilePath); // 获取文件输出对象 ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); // 将压缩后的 字节数组输出到文件 objectOutputStream.writeObject(bytesZip); // 将用到的编码文件写入压缩文件 objectOutputStream.writeObject(huoFuManCodeMap); //关闭资源 objectOutputStream.close(); fileOutputStream.close(); fileInputStream.close(); } public byte[] myZip(byte[] bytes) { // 0.统计每个字符对应了个数,并以节点的形式存放,List<Node> List<Node> nodes = strToList(bytes); // 1.将上一步中的List<Node>转为霍夫曼树 Node rootNode = listToTree(nodes); // preList(rootNode); // 2.将上一步中的霍夫曼树转变为霍夫曼编码表Map<bity,String> map Map<Byte, String> huoFuManCode = getHuoFuManCode(rootNode, "", new StringBuilder()); // 3.使用上一步中的霍夫曼表,对"i like like like java do you like a java",逐个翻译,生成霍夫曼编码后的数据,即霍夫曼表对应的字符串,如:"101010001011...." String huoHuManStr = strToHuoHuManStr(bytes, huoFuManCode); // 4.对上一步中的霍夫曼编码"101010001011....",每8位转为一个bite,最后生成byte[]数组 // bytes=>[-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28] byte[] bytesResult = huoHuManStrToByte(huoHuManStr); return bytesResult; } /** * 解压 * * @param bytes * @param huoFuManCode * @return */ public byte[] myUnZip(byte[] bytes, Map<Byte, String> huoFuManCode) { String huoFuManStr = bytToHuoFuManStr(bytes); byte[] bytes1 = huoFuManStrToByte(huoFuManStr, huoFuManCode); return bytes1; } /** * 霍夫曼字符串转变为byte数组 * * @param huoFuManStr * @param huoFuManCode */ public byte[] huoFuManStrToByte(String huoFuManStr, Map<Byte, String> huoFuManCode) { Map<String, Byte> huoFuManDecode = new HashMap<>(); // huoFuManCode的key 与 value 反转 for (Byte aByte : huoFuManCode.keySet()) { huoFuManDecode.put(huoFuManCode.get(aByte), aByte); } List<Byte> list = new ArrayList<>(); for (int i = 0; i < huoFuManStr.length(); ) { int count = 1; while (true) { String key = huoFuManStr.substring(i, i + count); Byte aByte = huoFuManDecode.get(key); if (aByte == null) { count++; } else { list.add(aByte); i = i + count; break; } } } byte[] bytes = new byte[list.size()]; for (int i = 0; i < list.size(); i++) { bytes[i] = list.get(i); } return bytes; } /** * 将一个byte 数组[-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28]转为二进制字符串"101010001011...." * * @param bytes * @return */ public String bytToHuoFuManStr(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String string; if (i == (bytes.length - 1)) { string = byteToString(bytes[i], false); } else { string = byteToString(bytes[i], true); } stringBuilder.append(string); } return stringBuilder.toString(); } /** * 将一个byte [-88]转为二进制"10101000" * * @param b * @param flag */ public String byteToString(byte b, boolean flag) { int temp = b; if (flag) { temp |= 256; } String binaryString = Integer.toBinaryString(temp); if (flag) { binaryString = binaryString.substring(binaryString.length() - 8); } return binaryString; } /** * 将霍夫曼字符串(可以看做是一个二进制字符串),如:"0001011..."按照每8位一个byte转为byte[] * * @param huoHuManStr * @return */ public byte[] huoHuManStrToByte(String huoHuManStr) { int length; if (huoHuManStr.length() % 8 == 0) { length = huoHuManStr.length() / 8; } else { length = huoHuManStr.length() / 8 + 1; } byte[] bytes = new byte[length]; // huoHuManStr 按照每8位截取 int startIndex = 0; for (int i = 0; i < bytes.length; i++) { String value; if (startIndex + 8 > huoHuManStr.length()) { value = huoHuManStr.substring(startIndex); } else { value = huoHuManStr.substring(startIndex, startIndex + 8); } bytes[i] = (byte) Integer.parseInt(value, 2); startIndex += 8; } return bytes; } /** * 根据霍夫曼编码表转为霍夫曼对应的字符串 * * @param bytes 原文,如"i like like like java do you like a java" * @param huoFuManCode 是一个翻译表:32=0001011, 97=0001011100.. * @return 返回对应字符串:0001011100100010110000001011100100010.... */ public String strToHuoHuManStr(byte[] bytes, Map<Byte, String> huoFuManCode) { StringBuffer huoHuManStr = new StringBuffer(); for (byte aByte : bytes) { String value = huoFuManCode.get(aByte); huoHuManStr.append(value); } return huoHuManStr.toString(); } /** * @param node 当前处理的节点 * @param code 0表示左边线路,1表示右边线路 * @param strCode 原来已经走过的线路 * @return */ public Map<Byte, String> getHuoFuManCode(Node node, String code, StringBuilder strCode) { if (node == null) { return huoFuManCodeMap; } StringBuilder strCodeNew = new StringBuilder(strCode); //将code 加入到 stringBuilder2 strCodeNew.append(code); if (node != null) { if (node.getKey() == null) { getHuoFuManCode(node.getLeftNode(), "0", strCodeNew); getHuoFuManCode(node.getRightNode(), "1", strCodeNew); } else { huoFuManCodeMap.put(node.getKey(), strCodeNew.toString()); } } return huoFuManCodeMap; } /** * 转为哈夫曼树 * * @param nodes */ public Node listToTree(List<Node> nodes) { while (nodes.size() > 1) { Collections.sort(nodes); Node leftNode = nodes.get(0); Node rightNode = nodes.get(1); Node parentNode = new Node(leftNode.getValue() + rightNode.getValue()); parentNode.setLeftNode(leftNode); parentNode.setRightNode(rightNode); nodes.remove(leftNode); nodes.remove(rightNode); nodes.add(parentNode); } return nodes.get(0); } public List<Node> strToList(byte[] bytes) { Map<Byte, Integer> map = new HashMap<>(); // a:5,表示a字母出现5次,使用节点存放为 Node{key=a,value=5} for (byte b : bytes) { byte key = b; Integer value = map.get(key); if (value == null) { map.put(key, 1); } else { map.put(key, value + 1); } } List<Node> list = new ArrayList<>(); for (byte key : map.keySet()) { list.add(new Node(key, map.get(key))); } return list; } public void preList(Node node) { if (node == null) { return; } System.out.println(node); if (node.getLeftNode() != null) { preList(node.getLeftNode()); } if (node.getRightNode() != null) { preList(node.getRightNode()); } } } /** * 节点 */ class Node implements Comparable<Node> { /** * a:5,表示a字母出现5次,使用节点存放为 Node{key=a,value=5} */ private Byte key; // value表示权重,即字母出现的次数 private Integer value; private Node rightNode; private Node leftNode; public Node(Integer value) { this.value = value; } public Node(Byte key, Integer value) { this.key = key; this.value = value; } @Override public int compareTo(Node o) { return this.value - o.value; } @Override public String toString() { return "Node{" + "key=" + key + ", value=" + value + '}'; } public Byte getKey() { return key; } public void setKey(Byte key) { this.key = key; } public Integer getValue() { return value; } public void setValue(Integer value) { this.value = value; } public Node getRightNode() { return rightNode; } public void setRightNode(Node rightNode) { this.rightNode = rightNode; } public Node getLeftNode() { return leftNode; } public void setLeftNode(Node leftNode) { this.leftNode = leftNode; } }
完美!
标签:解压,编码,bytes,value,Node,key,byte,霍夫曼,public 来源: https://www.cnblogs.com/butingxue/p/16215114.html