我们可以轻松地将String
反汇编到byte[]
String s = "my string";
byte[] b = s.getBytes();
System.out.println(new String(b)); // my string
当涉及压缩时,似乎存在一些问题。假设您有两种方法,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
其他原因,为什么不呢,我想修改第一种方法来返回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
为什么?需要做些什么来使其发挥作用?
答案 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.这可能会占用压缩的好处。