小明思考

Just a software engineer
posts - 124, comments - 36, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

Java 控制台中文问题(windows平台)

Posted on 2012-01-19 16:18 小明 阅读(3704) 评论(1)  编辑  收藏 所属分类: 开发日志
测试代码

 1 import java.io.Console;
 2 import java.lang.reflect.Constructor;
 3 import java.lang.reflect.Field;
 4 import java.nio.charset.Charset;
 5 
 6 public class JsTest {
 7     
 8     @SuppressWarnings("rawtypes")
 9     private static void outputCharset(){
10         Constructor[] ctors = Console.class.getDeclaredConstructors();
11         Constructor ctor = null;
12         for (int i = 0; i < ctors.length; i++) {
13             ctor = ctors[i];
14             if (ctor.getGenericParameterTypes().length == 0)
15                 break;
16         }
17         try {
18             ctor.setAccessible(true);
19             Console c = (Console) ctor.newInstance();
20             Field f = c.getClass().getDeclaredField("cs");
21             f.setAccessible(true);
22             System.out.println(String.format("Console charset:%s",
23                     f.get(c)));
24             System.out.println(String.format("Charset.defaultCharset():%s",
25                     Charset.defaultCharset()));
26         } catch (Exception x) {
27             x.printStackTrace();
28         }
29     }
30     
31     public static void main(String[] args) {
32         outputCharset();
33         System.out.println("1234中文5678");
34     }
35 }

编码
有两个编码需要提及的是:
1. Console charset(控制台编码)
控制台编码是windows的命令行窗口的编码,可以使用chcp来查看,也可以是窗口属性中看到。
我的是英文的Windows XP加上了中文支持。(在控制面板的Region中设置)

2. System default Charset(系统默认编码)
这个是系统默认编码,windows英文操作系统是cp1252(iso-8859-1)

试验
编译:
javac -encoding utf8 JsTest.java
因为我的源代码用utf8存贮,所以编译的时候加上了utf8选项。

测试1:
java  zzz.JsTest

Java把字符串直接通过default Charset[windows-1252(iso-8859-1)]转化后输出到控制台,但是这个控制台只能识别code936(gb2312)的编码,所以不能识别,
输出??

测试2:
指定file.encoding来改变default charset为gb2312,这下okay了
java -Dfile.encoding=gb2312 zzz.JsTest

测试3:
换成utf8看看,变成乱码。
Java -Dfile.encoding=utf8 zzz.JsTest

测试4:
通过chcp来改变控制台编码,行不通。

结论
1. 只有console charset 和 system default charset保持一致才不会乱码
2. chcp 65001(utf8)不能正常工作
3. 可以通过-Dfile.encoding改变default charset

解决方案
因为我们无法控制console的编码,如何能确保程序总是能正确输出,即使程序被放在日文OS上执行?

native unicode output
这种方案使用Windows Native API WriteConsoleW来输出,这样总是以unicode输出。(使用了JNA)

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;

/** For unicode output on windows platform
 * 
@author Sandy_Yin
 * 
 
*/
public class Console {
    
private static Kernel32 INSTANCE = null;

    
public interface Kernel32 extends StdCallLibrary {
        
public Pointer GetStdHandle(int nStdHandle);

        
public boolean WriteConsoleW(Pointer hConsoleOutput, char[] lpBuffer,
                
int nNumberOfCharsToWrite,
                IntByReference lpNumberOfCharsWritten, Pointer lpReserved);
    }

    
static {
        String os 
= System.getProperty("os.name").toLowerCase();
        
if (os.startsWith("win")) {
            INSTANCE 
= (Kernel32) Native
                    .loadLibrary(
"kernel32", Kernel32.class);
        }
    }

    
public static void println(String message) {
        
boolean successful = false;
        
if (INSTANCE != null) {
            Pointer handle 
= INSTANCE.GetStdHandle(-11);
            
char[] buffer = message.toCharArray();
            IntByReference lpNumberOfCharsWritten 
= new IntByReference();
            successful 
= INSTANCE.WriteConsoleW(handle, buffer, buffer.length,
                    lpNumberOfCharsWritten, 
null);
            
if(successful){
                System.out.println();
            }
        }
        
if (!successful) {
            System.out.println(message);
        }
    }
}



评论

# re: Java 控制台中文问题(windows平台)  回复  更多评论   

2012-01-23 23:13 by tb
很好啊

只有注册用户登录后才能发表评论。


网站导航: