现代计算机采用的都是冯.诺依曼体系结构,因此都具有相同的结构特征,拥有五大组成部分:输入数据和程序的输入设备,记忆程序和数据的存储器,完成数据加工处理的运算器,控制程序执行的控制器,输出处理结果的输出设备。JVM是一台虚拟的计算机,也有类似的特征。本系列文章研究的是java中文问题,跟输入输出有着密切的联系,为了突出重点,我们暂且将JVM的其它细节放下,只需了解JVM内部的数据是用Unicode表示的,使用的编码方式是UTF-16(至于是UTF-16LE还是UTF-16BE就要看具体的虚拟机实现了,intel x86 -windows 下是UTF-16LE,这可以使用 System.getProperty("sun.io.unicode.encoding") 取得)。
现在我们具体来看运行一个控制台程序所经历的步骤以及这个过程中涉及到输入和输出。一个程序从源代码到运行大概会经历这么一个过程:
1、使用一个文本编辑器编写java源代码,完毕后保存到一个.java文件中。如果指定文件的保存格式(GBK,UTF-8 ect.),则用指定的格式保存,否则使用默认编码方式保存(记事本,Editplus,eclipse等都是使用系统默认的编码方式GBK)。
2、使用javac命令编译.java源文件,产生.class文件,以UTF-8格式保存。注意,.class文件的格式必须是UTF-8,不需要指定,也不管系统默认的编码方式是什么。
3、使用java程序,运行jvm,载入编译好的.class文件,程序开始运行。
4、运行过程中,程序从输入(标准输入,文件,网络)中取得数据,进行相应的编码转换后放进JVM,以供运算使用。运算完毕后,将产生的数据进行编码转换后输出到指定位置(标准输出,文件,网络)
这样,程序就一直运行下去,直到结束。这期间,哪些地方有可能出现中文问题呢?下面一一道来:
1、使用javac进行编译时。如果我们.java源文件保存的编码方式跟javac指定的读入编码方式不一致,则会出现中文问题。譬如,我们在中文windows环境下用eclipse编写好源文件,然后到一个英文linux环境下进行编译,如果javac时没有指定编码方式为GBK,那么javac就会按照当前系统的默认编码方式(ISO-8859-1)进行解析,虽然里面的英文字符是不会出错的,但是中文就全部变成乱码了,也就是说,javac产生的.class文件中存储的中文字符是错误的。这样,运行的时候肯定也就出问题了。
建议 1 :保存.java源文件时使用UTF-8进行保存,在使用javac编译的时候通过参数 -encoding UTF-8指定编码方式。这样,可以保证源程序在任何支持UTF-8的平台上都能通过编译。PS:通过记事本的另存为将一个源文件保存成UTF-8时,会在文件头部加上一个BOM(ef bb bf),javac会报错。但是用Editplus(从Doucument菜单中选择Permanet Settings,有三个分类,分别是General,File,Tools.点击File,右边会有一项是 UTF-8 signature: 选择 always remove signature. 点击OK),eclipse却不会出现这种问题。
2、控制台跟操作系统密切相关,标准输入输出的编码都是固定的,也就是系统的默认编码,这是不能动态改变的。如果你程序里有中文编码,在中文环境下调用System.out.println("汉"); jvm会自动将输出流转换为GBK字节串交给控制台,控制台使用默认编码就正确输出了。但是该语句在英文环境下运行的话,那里的默认编码是ISO-8859-1,jvm就将“汉”转换成相应编码交给控制台,也就是两个“?”了。
建议 2:如果程序要跨平台的话,程序里用到控制台输出的代码最好不用中文字符。
3、如果数据来自网络或者文件的话,数据源的编码方式可以多种多样。因此,我们在读入的时候一定要清楚数据源的编码方式,通知jvm进行正确的处理,否则也会出现中文问题。下面以文件读写为例。
java 中处理字符的读写一般使用FileReader和FileWriter。但是这两个类都是使用系统默认的字符编码进行文件的读写,而且不能更改处理时的编码方式。也就是说,在GBK平台只能处理GBK的文件,在ISO-8859-1的平台只能处理ISO-8859-1的文件,这当然是不能接受的。因此,使用InputStreamReader 和 OutputStreamWriter吧。只要你能保证数据源的编码方式,然后读写时配置好相应的读写器的编码方式,就不会出现中文问题了。
建议 3 :使用文件进行数据交换时,最好统一文件的编码方式,如UTF-8。虽然对于中文来说,体积会增大50%,但是换来的是很好的跨平台特性。xml就是一个很好的例子。
如果能很好地做到上面几条,那么对于一个控制台应用程序来说应该是可以避免中文问题了。