Java字符乱码的问题,有时候真的会让人很头疼,所以有必要整理一下。之前尝试写过一次,但是发现很难讲得明白,这次试下用更简单的例子来说明问题。


编解码不一致

首先,我们可以通过调用java.nio.charset.Charset.defaultCharset()获得系统的默认字符集,中文Windows系统都是GBK,所以JVM默认都是以GBK字符集来进行编解码。

乱码产生最大的可能性在于编解码不一致。

// 代码片段1:
byte[] read = "你好abc".getBytes(); 
String result = new String(read);
System.out.println(result);

上诉这段代码一共有3步:
1、编码。这里为了看起来更简洁就这样写,事实上和你从文件/网络等其他媒介读取是一样的,原本输入流是什么类型编码,读取后也是什么类型编码。这里没有指定编码方式,所以默认为GBK。

2、解码。我们最终的操作都是字符串对象,可以通过指定字符串的对字节数组的解码方式,最终获得一个字符串对象。这里没有指定解码方式,所以默认为GBK。

3、输出、使用字符串。事实上这里还有一次编解码过程,就是输出流编码为GBK,控制台解码为GBK,最终显示。因为输出、使用字符串时都是以系统默认字符集进行,不存在编解码不一致的可能,所以这里不会是乱码根源。

假设我们将输入流的编码改一下:

// 代码片段2:
byte[] read = "你好abc".getBytes("utf-8"); 
String result = new String(read);
System.out.println(result);

再来分析一下3步:
1、编码,utf-8;
2、解码,gbk。
编解码不一致,这里便产生了乱码。

至于如何修复,相信已经很清楚了:

// 代码片段3:
byte[] read = "你好abc".getBytes("utf-8"); 
String result = new String(read,"utf-8");
System.out.println(result);

将解码方式改为与编码对应就可以了。

小结:
1、我们一般比较难控制输入流是什么编码,所以注意解码时与输入流的编码类型一致即可。我们一般在调用输入流的相关方法看到有charset参数,就是指解码方式。
2、输出流同样可以操作其编码方式,如果后续会以输入流操作这个输出的结果,比如说文件,只要都以默认的方式进行,便不会产生乱码。我们一般在调用输出流的相关方法看到有charset参数,就是指编码方式。


原本编码类型不支持

另外一个比较常见的问题就是原本的字符集根本不支持我们想要的字符。

// 代码片段4:
byte[] read = "你好abc".getBytes("iso-8859-1"); 
String result = new String(read,"iso-8859-1");
System.out.println(result);

上诉代码编码解码都一致,但还是出现了中文乱码,这是因为iso-8859-1字符集根本就不支持中文字符,所以遇到中文字符当然就乱码了。

这个问题比较经典的例子有:
在JavaWeb的Servlet中,由于response对象的getWriter()方法返回的PrintWriter对象默认使用“iso-8859-1”字符集进行字符串到字节数组的编码转换,所以客户端在进行输入解码的时候不管以什么类型解码都会乱码。为了防止乱码,可以先指定输出流的编码方式,然后客户端再以同样的utf-8进行解码。

response.setContentType(“text/plain;charset=utf-8”);
response.setCharacterEncoding(“UTF-8”);

乱码问题常见的产生原因:
1、编解码不一致;
2、原本编码类型不支持某种字符。