乱码的出现,通常是由于我们使用了错误的charset或者与平台相关的charset引起的,java是跨平台的,如果你指定了正确的,或者特定的某种编码,那么在任何时候,它都不会出现我们所不期望的乱码.
在java的io的操作过程中,包括System.out简单输出信息,都是依赖于我们所设置的charset或者默认charset(如果不设置)。
获得系统默认的charset:
import java.nio.charset.Charset;
public class HelloWorld{
public static void main(String[]args){
System.out.println("DefaultCharset="+Charset.defaultCharset().name());
Console console=System.console();
Field fieldOut=console.getClass().getDeclaredField("out");
fieldOut.setAccessible(true);
Object streamEncoder=fieldOut.get(console);
Field fieldCS=streamEncoder.getClass().getDeclaredField("cs");
fieldCS.setAccessible(true);
Charset consoleCharset=(Charset)fieldCS.get(streamEncoder);
System.out.println("ConsoleCharset="+consoleCharset.name());//x-mswin-936
System.out.println("===========");
System.out.println(Charset.defaultCharset().name());
String chineseChar="汉";
System.out.println(chineseChar);
}
}
上面代码的输出在我的电脑上是(java HelloWorld):
DefaultCharset=GBK
ConsoleCharset=x-mswin-936
===========
GBK
汉
说明:ConsoleCharset=x-mswin-936 这种charset是microsoft的中文系统的一种编码了,兼容GBK,所以我们可以认为其就是GBK
数据转换及输出过程解析:
汉(unicode是java在内存中的数据格式)---编码(DefaultCharset=GBK)为二进制--->二进制数据传递----解码(ConsoleCharset=x-mswin-936)-->显示
在这个过程中,有一个编码(encode)的步骤和一个解码(decode)的过程,如果这两个charset不一致,或者不兼容,那么乱码必然出现.
测试方式如下(即更改DefaultCharset为UTF-8,使DefaultCharset与ConsoleCharset不兼容,不一致)
java -Dfile.encoding=UTF-8 HelloWorld
输出为:
DefaultCharset=UTF-8
consoleCharset=x-mswin-936(注:此编码可等同于GBK)
===========
UTF-8
姹 (注:此处为乱码)
好了,乱码就这样子产生了.
回到eclipse我们也可以做类似的测试,不过代码要稍微改一下,因为eclipse的console与xp下面模拟dos的console是不一样的。
上面与Console对象有关的代码不可用。代码如下:
import java.nio.charset.Charset;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("DefaultCharset="+Charset.defaultCharset().name());
String chineseChar="汉";
System.out.println(chineseChar);
}
}
输出为:
DefaultCharset=GBK
汉
可能有一些人测试会输出乱码,后面将测试和说明.
eclipse也有console,那么它的console的编码在哪改呢?
就在这个HelloWorld上右键Run As->Run Configuration->Java Application->New->Common->Encoding 即为这个编码了。
我们改为UTF-8再运行上面的代码,输出为:
DefaultCharset=UTF-8
汉
这个地方有点迷惑人的是,当你修改了eclipse的console的Encoding的时候,它在运行一个java application的时候,把Default也改成一样的了
这样子,肯定是不会有乱码的,除非你最初的数据已经是乱码了.
我们可以在Run As->Run Configuration->Java Application上面New的那个选项下的Arguments->VM arguments 输入-Dfile.encoding=GBK
再运行上面的代码,输出将是:
DefaultCharset=GBK
��(乱码)
这下子,如愿以偿的,乱码出现了。
eclipse的console的encoding与xp下dos窗口的作用类似,只是一个输出,它们都有属于自己的编码
在eclipse下如果更改了console的encoding(默认是与项目的编码一致),那么你所运行的这个程序的DefaultCharset也会被更改成与其一致的编码,除非你在vm arguments加入类似-Dfile.encoding=GBK
汉(unicode )---编码(DefaultCharset)为二进制--->二进制数据传递----解码(ConsoleCharset=x-mswin-936)-->console显示
如果对console直接写入字节数组,这个将"汉"字变为数组的过程就是上面的编码过程,而如果我们直接将这个数组写入console,可以同样解决上面的code出现的乱码问题(DefaultCharset=GBK,Console Encoding=UTF-8)
public class HelloWorld {
public static void main(String[] args) {
System.out.println("DefaultCharset="+Charset.defaultCharset().name());
String chineseChar="汉";
System.out.println(chineseChar);
System.out.write(chineseChar.getBytes("UTF-8"));
//或者另外的一种写法如下:
OutputStreamWriter os=new OutputStreamWriter(System.out, "UTF-8");//指定了encode过程中使用的编码
os.write(chineseChar);
os.flush();
}
}
以上代码中,在直接输出乱码的情况下,而后面两种处理方式都可以正确的输出"汉"字
在eclipse中运行的程序,如果DefaultCharset 与Console的编码是一致的,并且是可以对你要输出的字符进行编码(对中文,UTF-8和GBK),那么一个字符串是可以直接被输出到Eclipse Console的,如果是乱码,那么也就是说明,保存在String里面的字符已经是乱码或者不是unicode编码的字符串。此时,要分析原字符的乱码产生原因,而不是一味的在System.out进行输出的时候,用各种charset进行试探性输出.
小结:
所有的数据都要依据某种编码转换为二进制存储,传输,再进行解码显示。如果这两个过程所用的charset不一致,就将可能出现乱码(如果是编码兼容,是不会的,如果不兼容,肯定会出现).
下一节,进行文件操作,并进行编码解码的问题,并对与汉字相关的几种编码进行简单的说明。