Java-java 乱码, 转码不成功的问题

发布于 2017-02-02 11:59:11 字数 119 浏览 1453 评论 3

遇到java里乱码的问题,会试一堆如:

new String(str.getBytes("gbk"),"utf8");

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

甜柠檬 2017-08-01 19:24:55

在你的数据上做了这样一些测试

 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解码的实现在解码不正确的字节时会生成固定的未知字符而导致破坏性结果。

夜无邪 2017-07-25 21:40:35

"解码不正确的字节时会生成固定的未知字符"是对的, 但是被乱码问题搞了几次后, 还是希望能彻底明白这里发生了什么.

今天有点时间, 继续用我问题里的数据来看.

1). gbk编码的"中文中"字节数组,按utf8解码转为unicode:

0xd6 0xd0 0xce 0xc4 0xd6 0xd0

二进制码为:

11010110 11010000 11001110 11000100 11010110 11010000

Unicode和UTF-8之间的转换关系表(https://zh.wikipedia.org/wiki/UTF-8)

UCS-4编码                     UTF-8字节流
U+00000000 – U+0000007F     0xxxxxxx
U+00000080 – U+000007FF     110xxxxx 10xxxxxx
U+00000800 – U+0000FFFF     1110xxxx 10xxxxxx 10xxxxxx
U+00010000 – U+001FFFFF     11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U+00200000 – U+03FFFFFF     111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U+04000000 – U+7FFFFFFF     1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

可以看出, 上述gbk的字节流是没法按照utf-8解码的(utf编码第二个字节以后为10开头); 那么java里怎么处理的呢? 直接全部变为unicode 65533(0xFFFD, 即"replacement character", 也就是这玩意"�"); 到此原有信息已经完全被破坏, 下面无论怎么转码都不可能拿到正确的字符串了.

2). utf8编码的"中文中"字节数组,按gbk解码转为unicode:

0xe4 0xb8 0xad 0xe6 0x96 0x87 0xe4 0xb8 0xad

二进制码为:

11100100 10111000 10101101 11100110 10010110 10000111 11100100 10111000 10101101

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域中文乱码问题里详细分析了此种问题.

灵芸 2017-03-05 05:28:33

utf8 编码是3字节表示汉字。gbk编码是双字节的。iso8859-1 就是所谓的拉丁字符。单字节的。无法表示汉字啊。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文