当 Java 中的字节转换为字符串时,幕后会发生什么?
What happens under the hood when bytes converted to String in Java?
我在 Java 中尝试将字节转换为字符串时遇到问题,代码如下:
byte[] bytes = {1, 2, -3};
byte[] transferred = new String(bytes, Charsets.UTF_8).getBytes(Charsets.UTF_8);
并且原始字节与传输的字节不一样,分别是
[1, 2, -3]
[1, 2, -17, -65, -67]
我曾经以为是UTF-8字符集映射为负数“-3”。所以我把它改成“-32”。但是传输的数组保持不变!
[1, 2, -32]
[1, 2, -17, -65, -67]
所以我非常想知道当我调用 new String(bytes) 时到底发生了什么:)
构造函数的文档中有一行:
This method always replaces malformed-input and unmappable-character sequences with this charset's default replacement string.
这绝对是罪魁祸首,因为 -3
在 UTF-8 中是无效的。顺便说一句,如果你真的有兴趣,你可以随时下载 rt.jar
的源代码,并调试它。
并非所有字节序列在 UTF-8 中都有效。
UTF-8 是一种智能方案,每个代码点的字节数可变,每个字节的形式表示同一代码点后面有多少其他字节。
参考this table:
现在让我们看看它如何适用于您的 {1, 2, -3}
:
字节1
(十六进制0x01
,二进制00000001
)和2
(十六进制0x02
,二进制00000010
)独立,没问题。
字节-3
(十六进制0xFD
,二进制11111101
)是一个6字节序列的起始字节(在current UTF-8 standard中实际上是非法的),但是你的字节数组没有这样的序列。
您的 UTF-8 无效。 Java UTF-8 解码器将此无效字节 -3
替换为 Unicode 代码点 U+FFFD REPLACEMENT CHARACTER (also see this)。在 UTF-8 中,代码点 U+FFFD 是十六进制 0xEF 0xBF 0xBD
(二进制 11101111 10111111 10111101
),在 Java 中表示为 -17, -65, -67
.
在Java中,byte
是有符号的,其中负值大于127。而您使用的那些(-3 = 0xFD,-32 = 0xE0)在UTF-8中无效,所以它们都被转换为 Unicode 代码点 U+FFFD REPLACEMENT CHARACTER
,它被转换回 UTF-8 为 0xEF = -17, 0xBF = -65, 0xBD = -67.
您不能期望随机字节值被正确解释为 UTF-8 文本。
您得到的编码值 [-17, -65, -67] 对应于 Unicode 代码点 0xFFFD
。如果您查找该代码点,the Unicode specification 会告诉您 0XFFFD
"used to replace an incoming character whose value is unknown or unrepresentable in Unicode." 正如其他人指出的那样,没有任何后续代码单元的 -3
是损坏的 UTF-8,所以这个角色很合适
我在 Java 中尝试将字节转换为字符串时遇到问题,代码如下:
byte[] bytes = {1, 2, -3};
byte[] transferred = new String(bytes, Charsets.UTF_8).getBytes(Charsets.UTF_8);
并且原始字节与传输的字节不一样,分别是
[1, 2, -3]
[1, 2, -17, -65, -67]
我曾经以为是UTF-8字符集映射为负数“-3”。所以我把它改成“-32”。但是传输的数组保持不变!
[1, 2, -32]
[1, 2, -17, -65, -67]
所以我非常想知道当我调用 new String(bytes) 时到底发生了什么:)
构造函数的文档中有一行:
This method always replaces malformed-input and unmappable-character sequences with this charset's default replacement string.
这绝对是罪魁祸首,因为 -3
在 UTF-8 中是无效的。顺便说一句,如果你真的有兴趣,你可以随时下载 rt.jar
的源代码,并调试它。
并非所有字节序列在 UTF-8 中都有效。
UTF-8 是一种智能方案,每个代码点的字节数可变,每个字节的形式表示同一代码点后面有多少其他字节。
参考this table:
现在让我们看看它如何适用于您的 {1, 2, -3}
:
字节1
(十六进制0x01
,二进制00000001
)和2
(十六进制0x02
,二进制00000010
)独立,没问题。
字节-3
(十六进制0xFD
,二进制11111101
)是一个6字节序列的起始字节(在current UTF-8 standard中实际上是非法的),但是你的字节数组没有这样的序列。
您的 UTF-8 无效。 Java UTF-8 解码器将此无效字节 -3
替换为 Unicode 代码点 U+FFFD REPLACEMENT CHARACTER (also see this)。在 UTF-8 中,代码点 U+FFFD 是十六进制 0xEF 0xBF 0xBD
(二进制 11101111 10111111 10111101
),在 Java 中表示为 -17, -65, -67
.
在Java中,byte
是有符号的,其中负值大于127。而您使用的那些(-3 = 0xFD,-32 = 0xE0)在UTF-8中无效,所以它们都被转换为 Unicode 代码点 U+FFFD REPLACEMENT CHARACTER
,它被转换回 UTF-8 为 0xEF = -17, 0xBF = -65, 0xBD = -67.
您不能期望随机字节值被正确解释为 UTF-8 文本。
您得到的编码值 [-17, -65, -67] 对应于 Unicode 代码点 0xFFFD
。如果您查找该代码点,the Unicode specification 会告诉您 0XFFFD
"used to replace an incoming character whose value is unknown or unrepresentable in Unicode." 正如其他人指出的那样,没有任何后续代码单元的 -3
是损坏的 UTF-8,所以这个角色很合适