java實現哈夫曼編碼檔案解壓
阿新 • • 發佈:2022-04-20
java實現哈夫曼編碼檔案解壓
解壓新增程式碼
/** * 解壓檔案 * * @param src 壓縮檔案的全路徑 * @param dstFile 壓縮後存放的路徑 */ private static void unZipFile(String src, String dstFile) { FileInputStream is = null; ObjectInputStream ois = null; FileOutputStream os = null; try { is = new FileInputStream(src); // 建立物件輸入流 ois = new ObjectInputStream(is); // 讀取 byte[] huffmanBtyes = (byte[]) ois.readObject(); Map<Byte,String> zipMap = (Map<Byte, String>) ois.readObject(); // 解碼 byte[] bytes = unZip(zipMap, huffmanBtyes); // 寫入目標檔案 os = new FileOutputStream(dstFile); os.write(bytes); } catch (Exception exception) { exception.printStackTrace(); } finally { try { is.close(); ois.close(); os.close(); } catch (IOException e) { e.printStackTrace(); } } }
測試方法
public static void main(String[] args) {
String srcFile = "D:\\圖片\\dst.zip";
String destFile = "D:\\圖片\\new.jpg";
unZipFile(srcFile,destFile);
System.out.println("解壓成功");
}
全程式碼
public class HuffmanEncodingDemo { public static void main(String[] args) { String srcFile = "D:\\圖片\\dst.zip"; String destFile = "D:\\圖片\\new.jpg"; unZipFile(srcFile,destFile); System.out.println("解壓成功"); } /** * 解壓檔案 * * @param src 壓縮檔案的全路徑 * @param dstFile 壓縮後存放的路徑 */ private static void unZipFile(String src, String dstFile) { FileInputStream is = null; ObjectInputStream ois = null; FileOutputStream os = null; try { is = new FileInputStream(src); // 建立物件輸入流 ois = new ObjectInputStream(is); // 讀取 byte[] huffmanBtyes = (byte[]) ois.readObject(); Map<Byte,String> zipMap = (Map<Byte, String>) ois.readObject(); // 解碼 byte[] bytes = unZip(zipMap, huffmanBtyes); // 寫入目標檔案 os = new FileOutputStream(dstFile); os.write(bytes); } catch (Exception exception) { exception.printStackTrace(); } finally { try { is.close(); ois.close(); os.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 壓縮檔案 * * @param src 壓縮檔案的全路徑 * @param dstFile 壓縮後存放的路徑 */ private static void zipFile(String src, String dstFile) { FileInputStream is = null; FileOutputStream os = null; ObjectOutputStream oos = null; try { // 建立IO流 is = new FileInputStream(src); // 建立和原始檔相同大小的byte陣列 byte[] bytes = new byte[is.available()]; // 讀取檔案 is.read(bytes); // 解壓 byte[] huffmanZip = huffmanZip(bytes); // 建立檔案輸出流存放壓縮檔案 os = new FileOutputStream(dstFile); // 建立和輸出流相關的物件輸出流 oos = new ObjectOutputStream(os); // 這裡以物件的方式進行輸出壓縮檔案,方便恢復 oos.writeObject(huffmanZip); // 最後將編碼表寫入 oos.writeObject(huffmanCodes); } catch (Exception e) { System.out.println(e); } finally { try { is.close(); os.close(); oos.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 解碼 * * @param huffmanCodes 編碼表 * @param bytes 需要解碼的位元組陣列 */ private static byte[] unZip(Map<Byte, String> huffmanCodes, byte[] bytes) { // 拿到對應的字串 StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { // 判斷是不是最後一個位元組 boolean flag = (i == bytes.length - 1); // 不是則為真 String s = byteToBitString(!flag, bytes[i]); stringBuilder.append(s); } // 拿到之後按照編碼表解碼 // 先將編碼表調換 HashMap<String, Byte> unzipMap = new HashMap<>(); Set<Map.Entry<Byte, String>> entries = huffmanCodes.entrySet(); for (Map.Entry<Byte, String> entry : entries) { unzipMap.put(entry.getValue(), entry.getKey()); } // 建立集合存放byte ArrayList<Byte> byteArrayList = new ArrayList<>(); // 解碼並存放 for (int i = 0; i < stringBuilder.length(); ) { // 小計數器 int count = 1; boolean flag = true; Byte b = null; while (flag) { // 從i開始取出n個位元組進行判斷 String value = stringBuilder.substring(i, i + count); b = unzipMap.get(value); if (b == null) { ++count; } else { flag = false; } } byteArrayList.add(b); i += count; } // 把list轉成陣列返回 byte[] result = new byte[byteArrayList.size()]; for (int i = 0; i < byteArrayList.size(); i++) { result[i] = byteArrayList.get(i); } return result; } /** * 將一個byte轉成一個二進位制的字串 * * @param flag 標誌是否需要補高位,true表示需要,false表示不需要 * @return 按補碼返回 */ public static String byteToBitString(boolean flag, byte b) { int temp = b; // 如果是最後一個位元組不需要補高位 if (flag) { // 按位與 temp |= 256; } String str = Integer.toBinaryString(temp); if (flag) { str = str.substring(str.length() - 8); } return str; } /** * 封裝,方便呼叫(位元組陣列) * * @param bytes 傳入的位元組陣列 * @return 經過哈夫曼編碼後的位元組陣列 */ private static byte[] huffmanZip(byte[] bytes) { // 轉成Node集合 List<Node> nodes = getNodes(bytes); // 建立哈夫曼樹 Node huffmanTree = createHuffmanTree(nodes); // 建立編碼表 HashMap<Byte, String> encoding = createEncoding(huffmanTree); // 壓縮並返回 return zip(bytes, huffmanCodes); } /** * 封裝,方便呼叫(字串) * * @param str 傳入的字串 * @return 經過哈夫曼編碼後的位元組陣列 */ private static byte[] huffmanZip(String str) { byte[] bytes = str.getBytes(); // 轉成Node集合 List<Node> nodes = getNodes(bytes); // 建立哈夫曼樹 Node huffmanTree = createHuffmanTree(nodes); // 建立編碼表 HashMap<Byte, String> encoding = createEncoding(huffmanTree); // 壓縮並返回 return zip(bytes, huffmanCodes); } /** * 將位元組陣列根據編碼表壓縮 * * @param bytes 被轉換的位元組陣列 * @return 壓縮後的陣列 */ private static byte[] zip(byte[] bytes, HashMap<Byte, String> huffmanCodes) { StringBuilder builder = new StringBuilder(); // 遍歷bytes for (byte b : bytes) { // 根據編碼表拿到對應的編碼 String code = huffmanCodes.get(b); builder.append(code); } // 編碼後再次壓縮,轉成byte陣列 // 先得到byte[]的長度 int length = builder.length(); int len; // 或者寫成 len = (length + 7) / 8; if (length % 8 == 0) { len = length / 8; } else { len = length / 8 + 1; } byte[] result = new byte[len]; // 每8位對應 int index = 0; for (int i = 0; i < length; i += 8) { String str; if (i + 8 < length) { // 夠八位 str = builder.substring(i, i + 8); } else { // 最後不夠八位 str = builder.substring(i); } // 轉成一個byte存入 result[index++] = (byte) Integer.parseInt(str, 2); } return result; } static HashMap<Byte, String> huffmanCodes = new HashMap<>(); static StringBuilder stringbuilder = new StringBuilder(); /** * 為了方便呼叫和使用,過載 */ private static HashMap<Byte, String> createEncoding(Node root) { if (root == null) { return null; } // 為了減少root的一次 createEncoding(root.left, "0", stringbuilder); createEncoding(root.right, "1", stringbuilder); return huffmanCodes; } /** * 根據哈夫曼樹建立對應的哈夫曼編碼表 * * @param node 傳入的節點 * @param code 路徑:左位元組為0,右子節點為1 * @param stringBuilder 用於拼接路徑 */ private static void createEncoding(Node node, String code, StringBuilder stringBuilder) { StringBuilder builder = new StringBuilder(stringBuilder); builder.append(code); if (node != null) { // 判斷是不是應該編碼的 if (node.data == null) { // 左右遞迴 createEncoding(node.left, "0", builder); createEncoding(node.right, "1", builder); } else { // 如果是 huffmanCodes.put(node.data, builder.toString()); } } } /** * 根據Node集合建立哈夫曼樹 */ private static Node createHuffmanTree(List<Node> nodes) { while (nodes.size() > 1) { Collections.sort(nodes); Node left = nodes.get(0); Node right = nodes.get(1); // 通過另一個構造器 Node parent = new Node(left.weight + right.weight); parent.left = left; parent.right = right; nodes.remove(left); nodes.remove(right); nodes.add(parent); } return nodes.get(0); } /** * 將字元陣列按出現個數轉換成一個個Node,並存到List返回 * * @param bytes 被轉換的字元陣列 * @return 轉換後的list */ private static List<Node> getNodes(byte[] bytes) { ArrayList<Node> nodes = new ArrayList<>(); // 遍歷統計出現的次數 HashMap<Byte, Integer> map = new HashMap<>(); for (byte b : bytes) { Integer integer = map.get(b); if (integer == null) { map.put(b, 1); } else { map.put(b, integer + 1); } } // 將map裡的元素轉成一個個Node Set<Map.Entry<Byte, Integer>> entries = map.entrySet(); for (Map.Entry<Byte, Integer> entry : entries) { Node node = new Node(entry.getKey(), entry.getValue()); // 並加入list nodes.add(node); } return nodes; } } /** * 節點類 */ class Node implements Comparable<Node> { Byte data; int weight; Node left; Node right; public Node(int weight) { this.weight = weight; } public Node(Byte data, int weight) { this.data = data; this.weight = weight; } /** * 前序遍歷 */ public void preOrder() { System.out.println(this); if (this.left != null) { this.left.preOrder(); } if (this.right != null) { this.right.preOrder(); } } @Override public int compareTo(Node o) { return this.weight - o.weight; } @Override public String toString() { return "Node{" + "data=" + data + ", weight=" + weight + '}'; } }