最近在玩Hadoop和Hive, 因为这类框架还处在必须使用命令行交互的阶段。 不得不使用Windows Command 来访问hive。 (要问我问什么要再windows上装hadoop, 因为我没有linux 虚拟机啊。。。)
测试的WIndows是2008 R2 英文版, 其实这方面的问题从有Windows以来就一直存在。
要正确的显示一个字符,需要有几个基本的概念。 首先要知道 文本文件存储时的字符集编码(例如UTF8),读取该文本文件的应用要知道这个编码才能正确解析其内容。其次负责界面显示的程序要有支持该种字符的字体才能将该种字符显示出来。 使用Windows Console时,Windows Console作为应用程序的输入输出设备,它也必须知道在其中运行的应用程序给它的字符串使用的是何种编码,并且有对应该种字符(如中文)字体才能正确在console窗口中显示出来。
测试场景
假设我们有一个文本文件,其中存了四个汉字“简体中文”, 以utf8 编码存在磁盘上。
然后我们写了一个java 类,把这个文本文件的内容读出来,再打印在控制台上。()
在Windows Console中运行这个Java类,看看其显示的内容是否正确。
首先Java程序中,文件读取的默认编码是根据操作系统环境而不同的,如果需要人为制定,可以再启动程序的时候使用-Dfile.encoding="utf8" ,所以没有问题。 (测试是否设置成功可以在程序中使用:CharSet.defaultCharSet())
当在Java程序中, 使用Sysout时,对外的输出编码默认也是由CharSet.defaultCharSet()决定的
因此我们知道Java输出给Console的字符串是以UTF8编码的。但是Console是否能正确显示呢? 很遗憾不能显示。
import java.nio.charset.Charset;
public class EncodingTest {
public static void main(String[] args) throws FileNotFoundException {
System.out.println("file.encoding=" + System.getProperty("file.encoding"));
System.out.println("Charset.defaultCharset="+Charset.defaultCharset());
System.out.println("中文字符");
}
}
在一个中文Windows的命令行窗口中运行下面的程序
java EncodingTest
运行结果如下:
ms正常显示了,可是我们注意到,之所以能正常显示的原因是默认的字符编码全部使用了GBK。
如果我们将Java内的默认字符编码改为UTF8,结果就不同了。 Console打印出了乱码。这是因为Console 仍然试图按照GBK来解析Java程序输出的UTF8编码的字符串。
java -Dfile.encoding="UTF-8" EncodingTest
使用CHCP命令可以看到当前的Console 所使用的字符集(Windows中这个概念叫 Code Page)
在中文操作系统下,默认的code page是936, 也即对应的是GBK字符集。
如果要让Console能够显示前面 Java程序的UTF8编码的输出,则需要修改这个值。
chcp 65001
执行这个命令后,发现Console 的字体自动变化了。
再次执行java -Dfile.encoding="UTF-8" EncodingTest, 结果如下:
仍然是乱码。 这是为什么呢?因为当前的Console窗口字体中不支持显示中文。
在Console窗口标题栏点右键-> properties, 在属性窗口中修改其使用的字体为Lucida Console
再次执行这个Java程序:
可以发现中文字符显示出来了,但是仍然有一些乱码。 说明System.out 输出的字符编码和Console的65001 仍然有不一致的地方。