使用霍夫曼编码(Java)压缩和解压缩小的.png文件时出现的问题



所以我有一个实现霍夫曼编码的Java类,我想用它来压缩和解压缩任何类型的文件。

下面是我的代码:
import java.io.*;
import java.util.*;
public class HuffmanCoding {
public static void main(String[] args) throws IOException {
String inputFilePath = "C:\Users\MAJ\eclipse-workspace\ProjectTwo\src\inputFile.png";
String encodedOutputFilePath = "C:\Users\MAJ\eclipse-workspace\ProjectTwo\src\encodedOutputFile.txt";
// get the frequencies of all the bytes in the file
byte[] data = fileToByteArray(inputFilePath);
Map<Byte, Integer> frequencyTable = getByteFrequencies(data);
// create a Huffman coding tree
Node root = createHuffmanTree(frequencyTable);
// create the table of encodings for each byte
Map<Byte, String> encodings = createEncodings(root);
// encode the input file and write the encoded output to the output file
encodeFile(data, encodings, encodedOutputFilePath);
String inputFileExtension = inputFilePath.substring(inputFilePath.lastIndexOf('.'));
String decompressedOutputFilePath = "C:\Users\MAJ\eclipse-workspace\ProjectTwo\src\decompressedOutputFile" + inputFileExtension;
decodeFile(encodedOutputFilePath, decompressedOutputFilePath, root);
}
public static byte[] fileToByteArray(String filePath) throws IOException {
// read the file
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(filePath));
byte[] data = inputStream.readAllBytes();
inputStream.close();
return data;
}

public static Map<Byte, Integer> getByteFrequencies(byte[] data) {
// map for storing the frequencies of the bytes
Map<Byte, Integer> frequencyTable = new HashMap<>();
// count the frequencies of the bytes
for (byte b : data) {
frequencyTable.put(b, frequencyTable.getOrDefault(b, 0) + 1);
}
return frequencyTable;
}
public static Node createHuffmanTree(Map<Byte, Integer> frequencyTable) {
// create a priority queue to store the nodes of the tree
PriorityQueue<Node> queue = new PriorityQueue<>(Comparator.comparingInt(n -> n.frequency));
// create a leaf node for each byte and add it to the priority queue
for (Map.Entry<Byte, Integer> entry : frequencyTable.entrySet()) {
queue.add(new Node(entry.getKey(), entry.getValue()));
}
// create the Huffman tree
while (queue.size() > 1) {
// remove the two nodes with the lowest frequency from the queue
Node left = queue.poll();
Node right = queue.poll();
// create a new internal node with these two nodes as children and the sum of their frequencies as the frequency
assert right != null;
Node parent = new Node(left.frequency + right.frequency, left, right);
// add the new internal node to the queue
queue.add(parent);
}
// the root node is the node remaining in the queue
return queue.poll();
}

// node class for the Huffman tree
static class Node {
int frequency;
byte character;
Node left;
Node right;
Node(int frequency, Node left, Node right) {
this.frequency = frequency;
this.left = left;
this.right = right;
}
Node(byte character, int frequency) {
this.character = character;
this.frequency = frequency;
}
}
public static Map<Byte, String> createEncodings(Node root) {
// map for storing the encodings of the bytes
Map<Byte, String> encodings = new HashMap<>();
// create the encodings
createEncodings(root, "", encodings);
return encodings;
}
private static void createEncodings(Node node, String encoding, Map<Byte, String> encodings) {
if (node == null) {
return;
}
if (node.character != 0) {
// this is a leaf node, so add the encoding to the map
encodings.put(node.character, encoding);
} else {
// this is an internal node, so recurse on the left and right children
createEncodings(node.left, encoding + "0", encodings);
createEncodings(node.right, encoding + "1", encodings);
}
}

public static void encodeFile(byte[] data, Map<Byte, String> encodings, String outputFilePath) throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter(outputFilePath));
// create a string builder for building the encoded string
StringBuilder sb = new StringBuilder();
// encode the data and add the encoded string to the string builder
for (byte b : data) {
String str = encodings.get(b);
if (str == null) {
str = "";
}
sb.append(str);
}
// write the encoded string to the output file
writer.write(sb.toString());
writer.close();
}


public static void decodeFile(String inputFilePath, String outputFilePath, Node root) throws IOException {
// read the encoded data from the input file
BufferedReader reader = new BufferedReader(new FileReader(inputFilePath));
String encodedData = reader.readLine();
reader.close();
// create the output file
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputFilePath));
// decode the data and write it to the output file
Node current = root;
for (int i = 0; i < encodedData.length(); i++) {
current = encodedData.charAt(i) == '0' ? current.left : current.right;
assert current != null;
if (current.left == null && current.right == null) {
outputStream.write(current.character);
current = root;
}
}
outputStream.close();
}


}

当压缩和解压缩。txt文件时,一切正常,但当压缩&解压缩一个大小为5 KB的小。png图像,输出的解压缩文件,这应该是一个相同的。png图像到原来的一个,有正确的大小,但当我试图打开它与任何类型的图像查看器它不加载,我似乎无法找出问题是什么,我假设同样的问题将发生与任何其他类型的文件(.mp4, .mp3, .jpeg, .exe等…)。请帮助我,如果你可以!

不能有" special ";字符,如果您希望能够编码所有可能的字节。你也不需要。叶子已经由空指针标识。如果您更改:

if (node.character != 0) {

:

if (node.left == null) {

那么就可以了

在你有一个工作的霍夫曼编码器和解码器之前,你还有一段路要走。您需要写入位而不是字节,这样您就不会大幅扩展数据而不是压缩它。完成这些后,现在您需要处理最后一个字节中的额外位,以确保解码器不会解码最后的一两个多余符号。要做到这一点,您需要在符号之前发送符号数,或者编码一个额外的流结束符号。您需要在压缩数据的开头表示和编码霍夫曼码,以便解码器能够解释这些代码。您需要通过将编码器和解码器设置为单独的程序来演示它们的工作,以便解码器必须进行的唯一事情是一个压缩文件。

最新更新