Java 字符串、字节[]和压缩

Java 字符串、字节[]和压缩,java,compression,Java,Compression,我们可以轻松地将字符串分解为字节[] String s = "my string"; byte[] b = s.getBytes(); System.out.println(new String(b)); // my string 然而,当涉及压缩时,似乎存在一些问题。假设您有两种方法,compress和uncompress(下面的代码可以正常工作) 我的测试工作如下(工作正常) 出于其他原因,我想修改第一个方法,以返回字符串,而不是字节[]。

我们可以轻松地将
字符串
分解为
字节[]

        String s = "my string";
        byte[] b = s.getBytes();
        System.out.println(new String(b)); // my string
然而,当涉及压缩时,似乎存在一些问题。假设您有两种方法,
compress
uncompress
(下面的代码可以正常工作)

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

出于其他原因,我想修改第一个方法,以返回
字符串,而不是
字节[]。

因此,我从
compress
方法返回新字符串(输出)
,并将我的测试修改为:

String text = "some text";
String compressedText = Compressor.compress(text);
assertEquals(Compressor.uncompress(compressedText.getBytes), text); //fails
此测试失败,原因是
java.util.zip.DataFormatException:标头检查不正确

为什么?需要做什么才能使它工作?

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

下面是一个最简单的例子:

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

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

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

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));
}
byte[]b=新字节[256];
对于(int i=0;i
如您所见,如果您将其转换回字节,一些代码将全部转换为相同的值。本示例不处理一个字符使用多个代码编码的编码,如UTF-8

一般来说,在没有提供适当的编码参数的情况下,应该避免调用
String.getBytes()
新字符串(byte[])
。没有一对一的编码,每个字节都成为相应的字符代码,除非你自己编码


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

我会使用DeflatorOutputStream和inflatorInputStream在字符串和字节之间进行转换,您应该指定一种特定的编码。您能提供一个无损转换的示例吗?另外,在我的例子中,字节确实代表一些字符串。@Jam:你是什么意思?我添加了一个转换中断的示例。无损转换可以将任何字节数组编码为可移植的ASCII字符串。@在示例代码
compress()
方法中,Jam将压缩数据作为字符串返回。压缩后的数据显然不是有效的文本。@亚历克斯:我不知道这些字节是从哪里来的。但它们确实代表了一些有效的UTF-8字符.Integer.tohextString(新字符串(新字节[]{-128},“UTF-8”).codepoint(0))=“fffd”。Unicode替换字符=U+FFFD。这就是字节的来源:)
new String(new byte[]{-128}, "UTF-8").getBytes("UTF-8")
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));
}