我已经为哈夫曼编码实现了一个类。该类将解析输入文件并从中构建一个霍夫曼树,并创建一个地图,其中每个不同的字符在文件中作为键出现,而字符的霍夫曼代码作为其值。
例如,让字符串“aravind_is_a_good_boy”成为文件中的唯一一行。当你构建霍夫曼树并为每个角色生成霍夫曼代码时,我们可以看到,对于角色'a',霍夫曼代码是'101'而对于角色'r',霍夫曼代码是'0101'等
我的目的是压缩文件。所以我不能写一个字符串,它是通过霍夫曼代码直接替换每个字符而创建的。因为,每个字符将被至少3个字符替换(每个'1'和'0'仍将作为字符写入文件,而不是位。所以我以为我会把它作为字节写到文件中,因为你无法将位写入文件。但是,'a'和'r'都写成'5'到文件中。这在尝试解压缩文件时会导致问题。
这就是我将一系列位转换为字节的方式:
public byte[] compressString(String s, CharCodeHashMap map) {
String byteString = "";
byte[] byteArr = new byte[s.length()];
int size = 0;
for (int i = 0; i < s.length(); i++) {
byteString += addPaddingZeros(map.getCompressedChar(s.charAt(i)));
byteArr[size++] = new BigInteger(byteString, 2).toByteArray()[0];
byteString = "";
}
return byteArr;
}
我尝试为每个哈希码添加前缀'1',以解决问题。但是,当你构建一个霍夫曼树,读取一个文件时,一些字符将超过8位。然后,问题是new BigInteger(byteString, 2).toByteArray()
在数组中将包含多于1个元素。(例如,如果'v'具有哈希码'11010001'并且new BigInteger(byteString, 2).toByteArray()
返回元素数组[0,-47] ]。)
有人可以建议我写一个文件的方式,这样文件就会被压缩,同时这些问题也会得到解决。
答案 0 :(得分:0)
问题是现代操作系统中的文件被建模为可索引的字节序列 1 。
所以你需要的是一种方法来编码你的文件表示可能不是 8的倍数的事实。这意味着比特流大小不一定是文件大小(以字节为单位)乘以8。
有多种解决方案:
有没有办法在没有使用某些位的情况下处理这个? AFAIK,No。
1 - 在较低级别,文件表示为由多个字节组成的磁盘块序列。因此,从物理存储的角度来看,压缩已经很小的文件(例如小于磁盘块)并不能实现任何目标。类似地,当表示被建模为字节序列时,保存或不保存(比如说)3比特处于无意义的边界......如果那是关于你的话。
答案 1 :(得分:0)
是的,您可以将位写入文件。实际上,您总是将位写入文件。唯一的问题是你一次写8位。
你需要的是一个位缓冲区,比如一个32位无符号变量,你可以在其中累加位。有另一个整数跟踪位缓冲区中有多少位。使用左移和/或(或加号)运算符将更多位放入位缓冲区,使用和右移位运算符将它们删除。只要位缓冲区中有8位或更多位,就会将这8位作为字节写入文件。最后,将剩余的位(如果有的话)写入文件作为最后一个字节。
因此,要将值中的位位添加到缓冲区:
bitBuffer |= value << bitCount;
bitcount += bits;
写入和删除可用字节:
while (bitCount >= 8) {
writeByte(bitBuffer & 0xff);
bitBuffer >>>= 8;
bitCount -= 8;
}
您需要确保在解码时,不要将最后一个字节中的填充位误认为是另一个代码。您可以在消息之前的消息中发送实际位数(或最后一个字节中的位数),也可以在字母表中添加符号,以获得自己的霍夫曼代码的流末尾,以及用那个结束消息。
您遇到的另一个问题是您还需要在编码符号之前将霍夫曼代码本身传输到解码器,以便解码器知道如何解码。查看“规范霍夫曼代码”,了解如何有效地处理它。