Java字符串编解码为何不总是对称?及两段转换代码差异解析
Java字符串编解码常见问题解答
一、为何Java中的字符串编解码不总是对称?
其实核心原因就是编解码过程中可能出现字符丢失或无法映射的情况,给你举几个常见场景:
- 当你要编码的字符串里包含某个字符集不支持的字符时,那些不支持的字符会被替换成默认占位符(比如
?),之后再解码的时候,占位符根本没法还原成原来的字符,自然就不对称了。比如你把带emoji的字符串用GB2312编码,emoji不在GB2312的字符范围内,编码后就变成?,再解码回来也只能是?,回不到原来的emoji了。 - 还有一种情况是不同字符集的编码规则差异,比如有些是变长编码、有些是定长,如果编解码用的字符集不匹配,中间出现乱码后,再转码可能会产生不可逆的字节序列,最后解码出来的内容完全不对路。
二、两段代码的差异分析
先把你给出的代码贴出来:
val f = "中国" // 第一段代码 println(new String(f.getBytes("GB2312"),"GB2312")) // 第二段代码 println(new String((new String(f.getBytes("GB2312"),"UTF8")).getBytes("UTF8"),"GB2312") )
咱们分别拆解两段代码的执行逻辑:
1. 第一段代码的执行流程
这是一套正确的同字符集编解码操作:
- 第一步:
f.getBytes("GB2312"):把字符串"中国"用GB2312编码成字节数组。"中国"在GB2312里是两个双字节字符,所以会得到4个字节的数组(具体值比如[-42, -48, -71, -6])。 - 第二步:
new String(..., "GB2312"):把刚才的GB2312字节数组用GB2312解码回字符串。因为编码和解码用的是同一个字符集,而且"中国"完全在GB2312的支持范围内,所以最终输出还是**"中国"**,和原字符串完全一致。
2. 第二段代码的执行流程
这中间多了一次错误的跨字符集转码,属于典型的“瞎折腾”,结果必然出问题:
- 第一步:和第一段一样,
f.getBytes("GB2312")得到GB2312编码的4字节数组。 - 第二步:
new String(..., "UTF8"):把GB2312的字节数组强行用UTF-8解码。这里的关键问题是——GB2312的字节序列不符合UTF-8的编码规则(UTF-8对多字节字符的首字节有特定的位要求),所以解码时会产生乱码字符(通常是�或者其他不可见的奇怪字符)。 - 第三步:
(...).getBytes("UTF8"):把刚才乱码的字符串再用UTF-8编码成字节数组。这时候的字节数组已经不是最初的GB2312字节数组了,因为乱码字符的UTF-8编码和原GB2312字节完全不同。 - 第四步:
new String(..., "GB2312"):把这个被篡改过的字节数组用GB2312解码,结果肯定不是原来的"中国",大概率是一堆毫无意义的乱码(比如类似"鍖椾含"这类字符)。
总结一下:第一段是合规操作,结果正确;第二段是错误的跨字符集转码,中间产生了不可逆的乱码,最终输出完全错误的内容。
内容的提问来源于stack exchange,提问作者dukyz
相关产品推荐
相关产品推荐

