Java中的编码问题:接收、转码、输出

来源:互联网 发布:域名转码工具 编辑:程序博客网 时间:2024/05/22 07:05

1 常见的问题

在从网络获取数据后,在JVM中进行解析运算时,最后输出的过程中都会遇到不同的编码转码问题。本文涉及的主要有如下问题:

  • Java发送HTTP请求后,接到服务器传回数据stream(如html文本),在Console中显示中文乱码。
  • Java将获取的数据输出为本地文件,在本地打开后中文显示乱码。

2 JVM从外部获得数据后的编码问题

2.1 关键理解

  • Java或者JVM、内存都是采用Unicode进行编码,一个char占2个bytes。
  • 在接收到IO字节流、向IO输出字节流的时候,都是以Unicode为桥梁,IO输入会先转为Unicode,输出会从Unicode转为相应编码。
  • 从服务器获取的数据,如一个html网页,meta数据中如果标记采用GBK编码,那么接收的IO byte[]中就是采用相应的编码方式表示中文的。
  • 所以从外部接收的数据,想让JVM自身能正确存储、显示,实质就是先正确利用发送方的编码方式来解读bytes,存为Unicode的char stream过程。

2.2 示例

通过这个过程了解Java在接受IO数据时的转码。GBK-> Unicode->UTF-8

//有一个URLConnectionURL realUrl = new URL("http://...");URLConnection connection = realUrl.openConnection();connection.setRequestProperty("accept", "*/*");            connection.setRequestProperty("connection", "Keep-Alive");            connection.setRequestProperty("user-agent",                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");//建立连接connection.connect();//假设服务器返回的html网页中meta标签charset="GBK"in = new BufferedReader(new InputStreamReader(            connection.getInputStream(),            "gbk")); //第二个参数标记用什么编码解读byte streamString result = "";String line;while ((line = in.readLine()) != null) {    result += line;}// 此时result在内存是以Unicode形式编码存储,而且解读正确// 最终result中的中文可以被正常打印再终端,在打印的时候,会根据环境设置,如JVM设置以UTF-8编码输出,则会将Unicode的char stream转为UTF-8的字符串再输出到终端。System.out.println(result);

3 JVM输出到文件的编码

3.1 关键理解

  • Java、JVM、内存中存储的char stream是以Unicode编码的,再次强调Unicode的桥梁作用。
  • 所以一串数据如果已经被正确解读为Unicode的char stream(此时console可以正常print)存储在内存中,也就和原来是什么编码无关了,输出时的主要工作是建立需求编码对应的Writer和对应编码的内容,char stream,用正确的Writer和正确的数据就能输出正确的文件。
  • 简单来说,一个String输出到文件过程,就是将Unicode的char stream转换为对应编码的char stream在输出的过程。

3.2 示例

// 通过Charset类可以查看JVM本地的编码格式,就是默认输出时的编码System.out.println(Charset.defaultCharset());// 建立输出流,假设要输出服务器返回的网页信息File file = new File("C:/Users/xxx/Documents/todo/Feedback.html");BufferedWriter bf = new BufferedWriter(new OutputStreamWriter(        new FileOutputStream(file),        "GBK") // 第二个参数说明了这个writer的编码方式,否则按照默认        );// 获取正确编码的char stream并输出String sr = "xxxx";String str = new String(sr.getBytes("GBK"), "GBK");    // 第一个参数表示用GBK的方式将Unicode char stream转为用GBK编码的byte[]    // 有了byte[]序列还需要转为char stream,第二个参数表示生成一个GBK的String,用于输出,这才是正常的。    // 不指定会用默认的编码方式(这里是UTF-8对字节数组再编码),输出还是UTF-8,且中文乱码。

4 JVM输出到命令提示符的编码

如果正常编译运行,用cmd通过java -jar的方式运行jar包是可以正常输出的。但如果在运行前,使用CHCP 65001这样的命令来切换显示编码到UTF-8,就会发现中文显示乱码。所以还是理解,如果以默认的方式存储的String或者自己解码的String都是以Unicode存在JVM中,在输出的时候,因为系统环境是GBK的,所以会自动以GBK的形式输出。
但如果String本身有自己的指定编码方式,那么就会直接输出其对应的byte stream到目标环境中。如从网络上获取的一段用UTF-8编码的HTTP返回的json串,在指定UTF-8的Eclipse环境中调试,console中的中文会显示正常。但是如果直接做成jar包在windows的cmd中运行,由于其环境是GBK的,就会显示乱码。解决方式如2.2实例,其中修改解码方式为:

//建立连接connection.connect();//用utf-8的方式解读字节流in = new BufferedReader(new InputStreamReader(            connection.getInputStream(),            "utf-8"));

正确存为内存的Unicode后,在cmd中的普通输出自然就正常了。

5 参考资料

为了简洁,其他内容可以看一些参考资料。

  • GBK/UTF-8编解码
  • java 中文乱码解决之道