字符串,byte []和压缩

时间:2012-08-01 15:50:38

标签: java compression

我们可以轻松地将String反汇编到byte[]

        String s = "my string";
        byte[] b = s.getBytes();
        System.out.println(new String(b)); // my string

当涉及压缩时,似乎存在一些问题。假设您有两种方法,compressuncompress(下面的代码可以正常工作)

public static byte[] compress(String data) 
             throws UnsupportedEncodingException, IOException {
    byte[] input = data.getBytes("UTF-8");
    Deflater df = new Deflater();
    df.setLevel(Deflater.BEST_COMPRESSION);
    df.setInput(input);

    ByteArrayOutputStream baos = new ByteArrayOutputStream(input.length);
    df.finish();
    byte[] buff = new byte[1024];
    while (!df.finished()) {
        int count = df.deflate(buff);
        baos.write(buff, 0, count);
    }
    baos.close();
    byte[] output = baos.toByteArray();

    return output;
}

public static String uncompress(byte[] input) 
            throws UnsupportedEncodingException, IOException,
        DataFormatException {
    Inflater ifl = new Inflater();
    ifl.setInput(input);

    ByteArrayOutputStream baos = new ByteArrayOutputStream(input.length);
    byte[] buff = new byte[1024];
    while (!ifl.finished()) {
        int count = ifl.inflate(buff);
        baos.write(buff, 0, count);
    }
    baos.close();
    byte[] output = baos.toByteArray();

    return new String(output);
}

我的测试工作如下(工作正常)

String text = "some text";
byte[] bytes = Compressor.compress(text);
assertEquals(Compressor.uncompress(bytes), text); // works

其他原因,为什么不呢,我想修改第一种方法来返回String而不是byte[].

所以我return new String(output)方法中的compress并将我的测试修改为:

String text = "some text";
String compressedText = Compressor.compress(text);
assertEquals(Compressor.uncompress(compressedText.getBytes), text); //fails

此测试因java.util.zip.DataFormatException: incorrect header check

而失败

为什么?需要做些什么来使其发挥作用?

2 个答案:

答案 0 :(得分:4)

String(byte[])构造函数是问题所在。您不能简单地获取任意字节,将它们转换为字符串然后返回字节数组。 String类根据所需的字符集对此byte执行复杂的编码。如果给定的字节序列不能表示,例如在Unicode中,它将被丢弃或转换为其他内容。只有当这些字节真正代表某些String(在某些编码中)时,从字节到bytes并返回到String的转换才是无损的。

这是一个最简单的例子:

new String(new byte[]{-128}, "UTF-8").getBytes("UTF-8")

以上返回-17, -65, -67,而127输入返回完全相同的输出。

答案 1 :(得分:1)

它失败了,因为您只是使用平台的当前编码从字节转换为字符串。因此,大多数字节将转换为它们的等效字符代码,但有些字节可能会被其他代码替换,具体取决于当前的编码。要查看字节发生了什么,只需运行:

byte[] b = new byte[256];
for(int i = 0; i < b.length; ++i) {
    b[i] = (byte)i;
}
String s = new String(b);

for(int i = 0; i< s.length(); ++i) {
    System.out.println(i + ": " + s.substring(i, i+1) + " " + (int)s.charAt(i));
}

正如您所看到的,如果将其转换回字节,则某些代码将全部转换为相同的值。此示例不处理使用UTF-8中的多个代码编码字符的编码。

一般情况下,应避免在不提供适当编码参数的情况下调用String.getBytes()new String(byte[])。并且没有一对一的编码,其中每个字节都成为相应的字符代码,除非您编写自己的代码。

如果您确实希望将压缩数据作为String处理,则使用base64表示或十六进制转储。但要注意,字符串表示需要两倍的内存,base64增加4/3的因子,十六进制甚至是因子2.这可能会占用压缩的好处。