Java byte[]和String转换问题

Posted by Codeboy on March 23, 2021

前言

byte[] 转换为 String 后,重新转换回 byte[] 后,和之前的数组是同一个?看一个测试case:

public class Test {
    public static void main(String[] args) {
        byte[] bytes = new byte[]{-28, -72, -83, -27, -101};
        printBytes(bytes);
        printBytes(new String(bytes).getBytes());
    }

    private static void printBytes(byte[] bytes) {
        if (bytes != null) {
            for (byte b : bytes) {
                System.out.print(b + " ");
            }
            System.out.println();
        }
    }
}

结果如下:

-28 -72 -83 -27 -101 
-28 -72 -83 -17 -65 -67 

以上测试在macOS系统上进行,Linux和Windows类似

分析

简单的从 String 的构造方法可以看出,没有设置编码的情况下,会采用系统默认编码,macOS上的为 UTF-8,解码过程中使用到一个关键的StringDecoder,来看一下StringDecoder 的构造方法:

private void StringDecoder(Charset cs, String rcn) {
    this.requestedCharsetName = rcn;
    this.cs = cs;
    this.cd = cs.newDecoder()
            .onMalformedInput(CodingErrorAction.REPLACE)
            .onUnmappableCharacter(CodingErrorAction.REPLACE);
    this.isTrusted = (cs.getClass().getClassLoader0() == null);
}

其中比较关键的是 CodingErrorAction.REPLACE,javadoc中的解释:

Action indicating that a coding error is to be handled by dropping the erroneous input, appending the coder's replacement value to the output buffer, and resuming the coding;
通过丢弃错误的输入,将编码器的替换值附加到输出缓冲区并恢复编码来指示要处理编码错误的操作;

针对错误的部分,会被进行替换,UTF-8 是变长编码,可以是单字节、双字节、三字节,当字节数组的编码符合 UTF-8,不会有问题,反之将会被替换修正;

解决

既然是编码的问题,可以通过单字节编码模式,获取到正确的结果,操作如下:

byte[] newBytes = new String(bytes, StandardCharsets.ISO_8859_1).getBytes(StandardCharsets.ISO_8859_1)

这里引入另一个问题,一个 String 转换为 byte[] 后,再转换为 String 会有问题么?答案是不会,因为转换为 byte[] 的字节编码是符合 UTF-8 的;

小结

java中通过 byte[] 转换为 String 时,可能因为一些编码规则,造成部分被替换,反向转换为 byte[] 后和之前不同;在转换时,可以通过指定 StandardCharsets.ISO_8859_1 等单字节编码来解决问题;

如有任何知识产权、版权问题或理论错误,还请指正。

转载请注明原作者及以上信息。