【问题标题】:Writing binary value to file for Huffman Encoding将二进制值写入文件以进行霍夫曼编码
【发布时间】:2015-10-12 17:11:19
【问题描述】:

我正在尝试使用 Huffman 编码实现文件压缩。目前,我将标题写入压缩文件的第一行,然后写入编码的二进制字符串(即具有二进制编码值的字符串)。

但是,我的文件大小并没有减小文件大小,而是增加了每个字符,例如“a”,我正在编写其相应的二进制文件,例如 01010001,它占用更多空间。

如何以减少空间的方式将其写入文件?

这是我的代码

public void write( String aWord ) {

        counter++;
        String content;
        byte[] contentInBytes;

        //Write header before writing file contents
        if ( counter == 1 )
        {
            //content gets the header in String format from the tree
            content = myTree.myHeader;
            contentInBytes = content.getBytes();

            try {
                fileOutputStream.write(contentInBytes);
                fileOutputStream.write(System.getProperty("line.separator").getBytes());
            } catch (IOException e) {
                System.err.println(e);
            }
        }

        //content gets the encoded binary in String format from the tree
        content = myTree.writeMe(aWord);
        contentInBytes = content.getBytes();


            try {
                fileOutputStream.write(contentInBytes);
                fileOutputStream.write(System.getProperty("line.separator").getBytes());
            } catch (IOException e) {
                System.err.println(e);
            }
        }

示例输入文件:

abc
aef
aeg

压缩文件:

{'g':"010",'f':"011",'c':"000",'b':"001",'e':"10",'a':"11"}
11001000
1110011
1110010

【问题讨论】:

  • 这个有调用代码吗?你是如何填充 myTree 的?
  • 是的,有一个包含字符及其值的链表,并且“内容”正在获取该特定行的正确二进制值。我唯一的问题是这里的空间,所以我需要以一种比现在占用更少空间的方式写入文件,因为我当前的压缩文件最终是原始文件大小的 4-5 倍
  • 因此您可以测试或登录以验证 myTree 具有唯一成员...例如"a" 不重复。
  • 是的,树中没有重复的实例。我只需要高效地完成写作。
  • 曾经的解决方案是使用 ASCII 字符集 0-255 将位分组为字节 (8).. 因为现在当您写入 0 时,实际上是在写入字节 48。

标签: java file compression huffman-code


【解决方案1】:

正如我从 cmets 收集到的,您正在编写文本,但您真正想要实现的是编写二进制数据。您目前拥有的是一个很好的 demo 用于霍夫曼编码,但对于实际压缩数据是不切实际的。

要实现压缩,您需要将霍夫曼符号输出为二进制数据,您当前输出字符串“11”表示“a”,您只需输出两位 11 .

我认为这是当前在 myTree.writeMe() 中编码的,您需要修改方法以返回字符串,但更适合二进制输出,例如字节[]。

这取决于你的树类的内部工作如何做到这一点。我假设您在内部使用了一些 StringBuilder 并在循环输入时简单地添加编码的符号字符串。您将需要一个能够处理单个位的容​​器,而不是 StringBuilder。唯一合适的类是 java.util.BitSet(在实践中,经常会为此编写一个专门的类,并使用专门的 API 快速完成此操作)。但为简单起见,让我们暂时使用 BitSet。

在 writeMe 方法中,原则上您将执行以下操作:

 BitSet buffer = new BitSet();
 int bitIndex = 0;
 loop over input symbols {
     huff_code = getCodeForSymbol(symbol)
     foreach bit in huff_code {
         buffer.put(bitIndex++, bit)
     }
 }
 return buffer.toByteArray();

如何有效地执行此操作取决于您在内部如何定义霍夫曼代码表。但原理很简单,循环遍历代码,判断每个位置是 1 还是 0,然后将它们放入 BitSet 中的连续索引处。

if (digits == '1') {
    buffer.set(bitIndex);
} else {
    buffer.clear(bitIndex);
}

您现在有了霍夫曼编码数据。但是生成的数据将无法正确解压缩,因为您当前正在处理 words 并且您没有写任何指示压缩数据实际 结束 的位置(您目前使用换行)。例如,如果您对“a”进行 3 次编码,则 BitSet 将包含 11 11 11。那是 6 位,但是当您转换为 byte[] 时,它会被填充为 8 位:0b11_11_11_00。

那些额外的、不可避免的部分会混淆你的解压。您将需要以某种方式处理这个问题,或者通过首先编码数据中的符号数量,或者通过使用显式的符号信号发送数据结束。

这应该让您知道如何继续。许多细节取决于您如何实现树类和编码符号。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-28
    • 2012-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多