尝试在Java中将字节转换为String时出现问题,代码如下:
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)时会发生什么:)
答案 0 :(得分:9)
并非所有字节序列在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解码器用Unicode代码点U+FFFD REPLACEMENT CHARACTER替换此无效字节-3
(另请参阅this)。在UTF-8中,代码点U + FFFD是十六进制0xEF 0xBF 0xBD
(二进制11101111 10111111 10111101
),在Java中表示为-17, -65, -67
。
答案 1 :(得分:4)
在Java中,byte
已签名,其中负值大于127.您使用的那些(-3 = 0xFD,-32 = 0xE0)在UTF-8中无效,因此它们都转换为Unicode代码点U+FFFD REPLACEMENT CHARACTER
,转换回UTF-8为0xEF = -17,0xBF = -65,0xBD = -67。
您不能指望随机字节值被正确解释为UTF-8文本。
答案 2 :(得分:2)
构造函数的文档中有一行:
此方法始终使用此charset的默认替换字符串替换格式错误的输入和不可映射字符序列。
这绝对是罪魁祸首,因为-3
在UTF-8中无效。顺便说一句,如果您真的感兴趣,可以随时下载rt.jar
的源代码并调试它。
答案 3 :(得分:1)
您获得的编码值[-17,-65,-67]对应于Unicode代码点0xFFFD
。如果您查找该代码点,the Unicode specification会告诉您0XFFFD
“用于替换其值在Unicode中未知或不可表示的传入字符。”正如其他人所指出的那样,没有任何后续代码单元的-3
被破坏了UTF-8,所以这个角色是合适的。