2

String我们可以byte[]轻松拆卸

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

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

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

没有别的原因,为什么不呢,我想修改第一个方法以返回 aString而不是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

这是为什么?需要做什么才能使其发挥作用?

4

2 回答 2

4

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

这是一个最简单的例子:

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

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

于 2012-08-01T15:55:45.967 回答
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[])不提供适当的编码参数。并且没有一对一的编码,每个字节都成为相应的字符代码,除非您自己编写代码。

如果您真的想将压缩数据作为字符串处理,请使用 base64 表示或十六进制转储。但请注意,字符串表示需要两倍的内存,base64 增加了 4/3 倍,十六进制甚至 2 倍。这可能会消耗压缩的好处。

于 2012-08-01T16:06:17.563 回答