java中的字节数组和字符串

asi*_*sim 3 java arrays

        byte arr[] = new byte[] {56, 99, 87, 77, 73, 90, 105, -23, -52, -85, -9, -55, -115, 11, -127, -127};
        String s= new String(arr);
        Arrays.equals(arr, s.getBytes()));  // returns false

Run Code Online (Sandbox Code Playgroud)

为什么数组不相等?我希望getBytes()返回原始字节数组。

rzw*_*oot 5

您似乎认为字节和字符是可以互换的。

他们根本就不是。

要将字符转换为字节,您可以使用“字符集编码”对字符进行“编码”。要将字节转换回字符,请使用“字符集编码”对其进行解码。没有字符集编码就不可能将一种转换为另一种

对于某些选定的编码系统,字节->字符->字节的转换只是“完美”的(保证总是返回相同的字节数组)。大多数编码系统不具有此属性。ISO-8859-1 是一种编码系统。然而,两种最常见的编码不具有​​此属性:UTF-8 和 US-ASCII 都无法完成此任务。

您在此处使用的方法(str.getBytes以及new String(byteArr))使用“平台默认编码”。从 JDK18 开始,保证是 UTF-8(从而保证这不会正常工作),在此之前,它是您系统的默认编码,我们不知道。

US-ASCII 不起作用,因为 US_ASCII 仅将所有字节的子集定义为“有效”:0-126。大多数字节(全部带有减号)都不是有效的 ASCII。

UTF-8 不起作用,因为并非所有字节序列都是有效的 UTF-8。换句话说,有些字节序列根本无法用 UTF_8 生成。

但更重要的是,整个原则被打破了。即使您知道它是 ISO-8859-1,您这样做想要实现什么目的?您也许能够将任意字节数组转换为 ISO-8859-1 并再次转换回来,而不会丢失任何内容,但这有什么意义呢?您可以轻松生成造成严重破坏的字符串,其中包含 NUL 字符、制表符、退格键、“铃声”和其他奇怪的内容。这是一个你永远不想打印的字符串。这就提出了一个问题:那你为什么想要一个?

这个问题确实只有一个合理的答案,那就是:我希望通过仅支持字符串的介质传输这些字节。例如,我有一些原始字节,我想将它们放入电子邮件中,或者放在 jira 票证的表单字段中或类似的愚蠢的东西中,并且出于某种原因,附件不是其中的选项。或者我想将其填充到 URL ( https://www.foo.bar/?q=raw-bytes-here) 中。

这样做有两个答案,但都不涉及new String(byteArr)

尼布尔斯

任何原始字节都可以简单地转换为十六进制表示形式:255(或-1,以有符号字节形式,它是同一件事)转换为FF. 1 变成 01 - 所有字节的长度始终恰好是 2 个字符。您可以使用:

byte f = -1;
String nibbled = String.format("%02X", (int) f);
System.out.println(nibbled); // prints 'FF'
Run Code Online (Sandbox Code Playgroud)

单个字母/数字(0-9A-F。从技术上讲,这只是一个数字,十六进制,其中 AF 也是数字)被称为“半字节”(因为它是半个字节,请参阅。天啊,这些术语在 60 年代出现时)发明是一件很有趣的事情不是吗)。

这有点低效;X 字节的字节数组变成 2*X 个字符的字符串(每个字符很可能占用 2 个字节,例如,如果它是 UTF-16 编码的,则总共效率为 25%,哎呀)。但它很容易阅读并且很常见。它非常适合短(不到 500 字节左右)字节数组。

另一个优点是,如果您可以读取十六进制,并且如果有符号相关,则可以读取 2 的补码,您可以查看字符串并知道数据是什么,这并不太困难。

Base64

Base64 是一种简单的编码方案,它定义了 64 个“安全”字符,您知道这些字符将安全地“生存”而不会被破坏或误解。这为每个字符提供了 6 位数据。字节为 8,因此,您可以通过这种方式将 3 个字节“填充”为 4 个字符;例如,900 字节数组会变成 1200 个字符。

Java内置了 base64 编码/解码

byte arr[] = new byte[] {56, 99, 87, 77, 73, 90, 105, -23, -52, -85, -9, -55, -115, 11, -127, -127};
String s = Base64.getEncoder().encodeToString(arr);
// s is all ASCII chars and safe to include just about everywhere.
// URL parameter, emails, web forms, you name it.
byte[] arr2 = Base64.getDecoder().decode(s);
Arrays.equals(arr, arr2); // true, guaranteed.
Run Code Online (Sandbox Code Playgroud)

Base64 稍微复杂一些,您不能再只看到 Base64 字符串而只看到字节矩阵样式。但它比半字节形式更有效:效率为 75%(如果底层字符每个字符占用 2 个字节,即使用 UTF-16,效率为 37.5%)。