我能夠壓縮各種文件(.jpg,.mp4等),但當我嘗試解壓縮這些非文本文件時程序只是返回一個空的解壓文件......奇怪的是我能夠解壓純文本文件就好了。Java - 我的霍夫曼解壓縮拒絕解壓縮非文本文件(返回空文件)
當我壓縮我的原始文件時,我把重建樹和編碼位所需的數據放在同一個文件中。格式看起來是這樣的:
<n><value 1><frequency 1>...<value n><frequency n>[the compressed bytes]
其中N是唯一字節(在我的樹葉的AKA數)總數值是字節形式和頻率只是我的葉值是每個字節的頻率/「字符」(頻率是一個int值,因此它由每個頻率4個字節組成)。
我的代碼中的BitFileReader和BitFileWriter只是BufferedOutStream/InputStream的包裝類,並且增加了一點一點的讀/寫功能。
我在下面添加了我的整個霍夫曼代碼,但主要關注的是底部的compress()和decompress()方法。至少我想知道這些方法的邏輯是否正確,如果是這樣,是什麼原因導致它在解壓縮其他文件類型(非純文本文件)時返回空的解壓縮文件?
霍夫曼代碼:
public class HuffmanCode {
public static Tree buildTree(int[] charFreqs) {
PriorityQueue<Tree> trees = new PriorityQueue<Tree>();
for (int i = 0; i < charFreqs.length; i++){
if (charFreqs[i] > 0){
trees.offer(new Leaf(charFreqs[i], i));
}
}
//assert trees.size() > 0;
while (trees.size() > 1) {
Tree a = trees.poll();
Tree b = trees.poll();
trees.offer(new Node(a, b));
}
return trees.poll();
}
public static void printStruct(Tree tree) {
//assert tree != null;
if (tree instanceof Leaf) {
Leaf leaf = (Leaf)tree;
System.out.println(leaf.value + " " + leaf.frequency);
} else if (tree instanceof Node) {
Node node = (Node)tree;
// traverse left
printStruct(node.left);
// traverse right
printStruct(node.right);
}
}
public static void printStruct(Tree tree, StringBuffer prefix) {
//assert tree != null;
if (tree instanceof Leaf) {
Leaf leaf = (Leaf)tree;
System.out.println(leaf.value + "\t" + leaf.frequency + "\t" + prefix);
} else if (tree instanceof Node) {
Node node = (Node)tree;
// traverse left
prefix.append('0');
printStruct(node.left, prefix);
prefix.deleteCharAt(prefix.length()-1);
// traverse right
prefix.append('1');
printStruct(node.right, prefix);
prefix.deleteCharAt(prefix.length()-1);
}
}
public static void fillEncodeMap(Tree tree, StringBuffer prefix, TreeMap<Integer, String> treeMap) {
//assert tree != null;
if (tree instanceof Leaf) {
Leaf leaf = (Leaf)tree;
treeMap.put(leaf.value, prefix.toString());
} else if (tree instanceof Node) {
Node node = (Node)tree;
// traverse left
prefix.append('0');
fillEncodeMap(node.left, prefix, treeMap);
prefix.deleteCharAt(prefix.length()-1);
// traverse right
prefix.append('1');
fillEncodeMap(node.right, prefix, treeMap);
prefix.deleteCharAt(prefix.length()-1);
}
}
public static void fillDecodeMap(Tree tree, StringBuffer prefix, TreeMap<String, Integer> treeMap) {
//assert tree != null;
if (tree instanceof Leaf) {
Leaf leaf = (Leaf)tree;
treeMap.put(prefix.toString(), leaf.value);
} else if (tree instanceof Node) {
Node node = (Node)tree;
// traverse left
prefix.append('0');
fillDecodeMap(node.left, prefix, treeMap);
prefix.deleteCharAt(prefix.length()-1);
// traverse right
prefix.append('1');
fillDecodeMap(node.right, prefix, treeMap);
prefix.deleteCharAt(prefix.length()-1);
}
}
public static void compress(File file){
try {
Path path = Paths.get(file.getAbsolutePath());
byte[] content = Files.readAllBytes(path);
TreeMap<Integer, String> encodeMap = new TreeMap<Integer, String>();
File nF = new File(file.getName()+"_comp");
nF.createNewFile();
BitFileWriter bfw = new BitFileWriter(nF);
int[] charFreqs = new int[256];
// read each byte and record the frequencies
for (byte b : content){
charFreqs[b&0xFF]++;
}
// build tree
Tree tree = buildTree(charFreqs);
// build TreeMap
fillEncodeMap(tree, new StringBuffer(), encodeMap);
// Writes tree structure in binary form to nF (new file)
bfw.writeByte(encodeMap.size());
for(int i=0; i<charFreqs.length; i++){
if(charFreqs[i] != 0){
ByteBuffer b = ByteBuffer.allocate(4);
b.putInt(charFreqs[i]);
byte[] result = b.array();
bfw.writeByte(i);
for(int j=0; j<4;j++){
bfw.writeByte(result[j]&0xFF);
}
}
}
// Write compressed data
for(byte b : content){
String code = encodeMap.get(b&0xFF);
for(char c : code.toCharArray()){
if(c == '1'){
bfw.write(1);
}
else{
bfw.write(0);
}
}
}
bfw.close();
System.out.println("Compression successful!");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void decompress(File file){
try {
BitFileReader bfr = new BitFileReader(file);
int[] charFreqs = new int[256];
TreeMap<String, Integer> decodeMap = new TreeMap<String, Integer>();
File nF = new File(file.getName()+"_decomp");
nF.createNewFile();
BitFileWriter bfw = new BitFileWriter(nF);
DataInputStream data = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
int uniqueBytes;
int counter = 0;
int byteCount = 0;
uniqueBytes = data.readUnsignedByte();
// Read frequency table
while (counter < uniqueBytes){
int index = data.readUnsignedByte();
int freq = data.readInt();
charFreqs[index] = freq;
counter++;
}
// build tree
Tree tree = buildTree(charFreqs);
// build TreeMap
fillDecodeMap(tree, new StringBuffer(), decodeMap);
// Skip BitFileReader position to actual compressed code
bfr.skip(uniqueBytes*5);
// Get total number of compressed bytes
for(int i=0; i<charFreqs.length; i++){
if(charFreqs[i] > 0){
byteCount += charFreqs[i];
}
}
// Decompress data and write
counter = 0;
StringBuffer code = new StringBuffer();
while(bfr.hasNextBit() && counter < byteCount){
code.append(""+bfr.nextBit());
if(decodeMap.containsKey(code.toString())){
bfw.writeByte(decodeMap.get(code.toString()));
code.setLength(0);
counter++;
}
}
bfw.close();
bfr.close();
data.close();
System.out.println("Decompression successful!");
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
File f = new File("test");
compress(f);
f = new File("test_comp");
decompress(f);
}
}
編輯:我找到了原因,但我不知道如何解決,或爲什麼會發生。問題是我的解壓縮方法中的charFreqs []數組永遠不會被填充(它的所有值仍然爲零)。根據數組,所有字節都有零個頻率。