Java-java 乱码, 转码不成功的问题
遇到java里乱码的问题,会试一堆如:
new String(str.getBytes("gbk"),"utf8");
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
遇到java里乱码的问题,会试一堆如:
new String(str.getBytes("gbk"),"utf8");
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(3)
在你的数据上做了这样一些测试
System.out.print("[");
for (char c : new String(gbk, "iso-8859-1").toCharArray()) System.out.printf("%x, ", (int)c);
System.out.println("]");
System.out.println(Arrays.toString(new String(gbk, "iso-8859-1").getBytes("iso-8859-1")));
System.out.print("[");
for (char c : new String(gbk, "utf-8").toCharArray()) System.out.printf("%x, ", (int)c);
System.out.println("]");
System.out.println(Arrays.toString(new String(gbk, "utf-8").getBytes("utf-8")));
System.out.print("[");
for (char c : new String(gbk, "gbk").toCharArray()) System.out.printf("%x, ", (int)c);
System.out.println("]");
System.out.println(Arrays.toString(new String(gbk, "gbk").getBytes("gbk")));
System.out.println();
System.out.print("[");
for (char c : new String(utf8, "iso-8859-1").toCharArray()) System.out.printf("%x, ", (int)c);
System.out.println("]");
System.out.println(Arrays.toString(new String(utf8, "iso-8859-1").getBytes("iso-8859-1")));
System.out.print("[");
for (char c : new String(utf8, "utf-8").toCharArray()) System.out.printf("%x, ", (int)c);
System.out.println("]");
System.out.println(Arrays.toString(new String(utf8, "utf-8").getBytes("utf-8")));
System.out.print("[");
for (char c : new String(utf8, "gbk").toCharArray()) System.out.printf("%x, ", (int)c);
System.out.println("]");
System.out.println(Arrays.toString(new String(utf8, "gbk").getBytes("gbk")));
输出是
[d6, d0, ce, c4, d6, d0, ]
[-42, -48, -50, -60, -42, -48]
[fffd, fffd, fffd, fffd, fffd, fffd, ]
[-17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67]
[4e2d, 6587, 4e2d, ]
[-42, -48, -50, -60, -42, -48]
[e4, b8, ad, e6, 96, 87, e4, b8, ad, ]
[-28, -72, -83, -26, -106, -121, -28, -72, -83]
[4e2d, 6587, 4e2d, ]
[-28, -72, -83, -26, -106, -121, -28, -72, -83]
[6d93, e15f, 6783, 6d93, fffd, ]
[-28, -72, -83, -26, -106, -121, -28, -72, 63]
那么问题就出现在utf-8解码gbk的字节时完全无法正确解码而生成了一些未知字符,这些字符也无法被还原成原本的字节。另外其实gbk解码uft-8的字节的时候是由于字节数是奇数个而造成了破坏,字节数是偶数个时是可以还原回去的。
assertEquals("中文", new String(new String("中文".getBytes("utf-8"), "gbk").getBytes("gbk"), "utf-8"));
最后的结论就是String的这个构造函数所使用的utf-8解码的实现在解码不正确的字节时会生成固定的未知字符而导致破坏性结果。
"解码不正确的字节时会生成固定的未知字符"是对的, 但是被乱码问题搞了几次后, 还是希望能彻底明白这里发生了什么.
今天有点时间, 继续用我问题里的数据来看.
1). gbk编码的"中文中"字节数组,按utf8解码转为unicode:
二进制码为:
Unicode和UTF-8之间的转换关系表(https://zh.wikipedia.org/wiki/UTF-8)
可以看出, 上述gbk的字节流是没法按照utf-8解码的(utf编码第二个字节以后为10开头); 那么java里怎么处理的呢? 直接全部变为unicode 65533(0xFFFD, 即"replacement character", 也就是这玩意"�"); 到此原有信息已经完全被破坏, 下面无论怎么转码都不可能拿到正确的字符串了.
2). utf8编码的"中文中"字节数组,按gbk解码转为unicode:
二进制码为:
gbk 编码规则为见 http://en.wikipedia.org/wiki/GBK.
我观察了半天, 初步结论是, "任何utf8编码的字节流里拿出两个连续的字节, 都是 有效的gbk编码". 即0xe4 0xb8, 被按gbk转为unicode 0x6d93, 即"涓"; 这也是另一种形式的乱码, 虽然是有效的汉字, 但不是我们想要的. 这时可以再按gbk转码回去, 不会有信息的丢失.
但是如果utf8字节数是奇数个; 那么按gbk解码, 势必我们需要把最后一个字节单独来解码, 就是我们上述utf8字节流的最后一个字节"0xad"; gbk里,单字节只有00—7F才是有效编码(即ascii码); 那么0xad直接转为unicode 65533(0xFFFD, 即"replacement character", 也就是这玩意"�"); 到此最后一个字节的原有信息已经完全被破坏, 下面无论怎么转码都不可能拿到正确的字符串了.
3). 把gbk或utf8编码的字节流按iso8859-1处理:
iso8859-1处理单字节0x00-0xff, 所以gbk或utf8编码的字节流 按iso8859-1编解码绝不会有信息的丢失. 我在@Java中HttpURLConnection返回头Location域中文乱码问题里详细分析了此种问题.
utf8 编码是3字节表示汉字。gbk编码是双字节的。iso8859-1 就是所谓的拉丁字符。单字节的。无法表示汉字啊。