Java编码问题解决方案大揭密

简介:
一、Java 编码是怎么回事?
对于使用中文以及其他非拉丁语系语言的开发人员来说,经常会遇到字符集编码问题。对于Java 语言来说,在其内部使用的是UCS2 编码(2 个字节的Unicode 编码)。这种编码并不属于某个语系的语言编码,它实际上是一种编码格式的世界语。在这个世界上所有可以在计算机中使用的语言都有对应的UCS2 编码。
正是因为Java 采用了UCS2 ,因此,在Java 中可以使用世界上任何国家的语言来为变量名、方法名、类起名,如下面代码如下:

class  中国
{
    
public  String 雄起()
    {
         
return   " 中国雄起 " ;
    }
}

中国 祖国 
=   new  中国();
System.out.println(祖国.雄起());

     哈哈,是不是有点象“中文编程”。实际上,也可以使用其他的语言来编程,如下面用韩文和日文来定义个类:

class  수퍼맨
{
    
public   void  スーパーマン() {  }
}
     实际上,由于Java 内部使用的是UCS2 编码格式,因为,Java 并不关心所使用的是哪种语言,而只要这种语言在UCS2 中有定义就可以。
     UCS2 编码中为不同国家的语言进行了分页,这个分页也叫“代码页”或“编码页”。中文根据包含中文字符的多少,分了很多代码页,如cp935 cp936 等,然而,这些都是在UCS2 中的代码页名,而对于操作系统来说,如微软的windows ,一开始的中文编码为GB2312 ,后来扩展成了GBK 。其实GBK cp936 是完全等效的,用它们哪个都行。
二、Java 编码转换

    
上面说了这么多,在这一部分我们做一些编码转换,看看会发生什么事情。
     先定义一个字符串变量:

    String gbk = "
中国"; //  “中国”在Java 内部是以UCS2 格式保存的
     用下面的语言输出一定会输出中文:
System.out.println(gbk);
     实现上,当我们从IDE 输入“中国”时,用的是java 源代码文件保存的格式,一般是GBK ,有时也可是utf-8 ,而在Java 编译程序时,会不由分说地将所有的编码格式转换成utf-8 编码,读者可以用UltraEdit 或其他的二进制编辑器打开上面的“中国.class ”,看看所生成的二进制是否有utf-8 的编码(utf-8 ucs2 之间的转换非常容易,因为utf-8 ucs2 之间是用公式进行转换的,而不是到代码页去查,这就相当于将二进制转成16 进制一样,4 个字节一组)。如“中国”的utf-8 编码按着GBK 解析就是“涓   浗 ”。如下图所示。



如果使用下面的语言可以获得“中国”的utf-8 字节,结果是6 (一个汉字由3 个字节组成)
System.out.println(gbk.getBytes("utf-8").length);
下面的代码将输出“涓   浗 ”。
System.out.println(new String(gbk.getBytes("utf-8"), "gbk"));   
由于将“中国“的utf-8 编码格式按着gbk 解析,所以会出现乱码。
如果要返回中文的UCS2 编码,可以使用下面的代码:
System.out.println(gbk.getBytes("unicode")[2]);
System.out.println(gbk.getBytes("unicode")[3]);
前两个字节是标识位,要从第3 个字节开始。还有就是其他的语言使用的编码的字节顺序可能不同,如在C# 中可以使用下面的代码获得“中国“的UCS2 编码:

String s = "
";
MessageBox.Show(ASCIIEncoding.Unicode.GetBytes(s)[0].ToString());
MessageBox.Show(ASCIIEncoding.Unicode.GetBytes(s)[1].ToString());
     使用上面的java 代码获得的“中“的16 进制UCS2 编码为4E2D ,而使用C# 获得的相应的ucs2 编码为2D4E ,这只是C# Java 编码内部使用的问题,并没有什么关系。但在C# Java 互操作时要注意这一点。
     如果使用下面的java 编码将获得16 进制的“中”的GBK 编码:
System.out.println(Integer.toHexString(0xff & xyz.getBytes("gbk")[0]));
System.out.println(Integer.toHexString(0xff & xyz.getBytes("gbk")[1]));
“中”的ucs2 编码为2D4E GBK 编码为D6D0
     读者可访问如下的url 自行查验:

    http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP936.TXT
     当然,感兴趣的读者也可以试试其他语言的编码,如“人类”的韩语是“ 인간의 ”,如下面的代码将输出“ 인간의 ”的cp949 ucs2 编码,其中cp949 是韩语的代码页。

String korean  =   " 인간의 " //  共三个韩文字符,我们只测试第一个“인”

System.out.println(Integer.toHexString(
0xff   &  korean.getBytes( " unicode " )[ 2 ]));

System.out.println(Integer.toHexString(
0xff   &  korean.getBytes( " unicode " )[ 3 ]));

System.out.println(Integer.toHexString(
0xff   &  korean.getBytes( " Cp949 " )[ 0 ]));

System.out.println(Integer.toHexString(
0xff   &  korean.getBytes( " Cp949 " )[ 1 ]));
 
上面代码的输出结果如下:
c7
78
c0
ce
     也就是说“ ”的 ucs2 编码为 C778 cp949 的编码为 C0CE ,要注意的是,在 cp949 中, ucs2 编码也有 C0CE ,不要弄混了。读者可以访问下面的 url 来验证:

http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP949.TXT

Java支持的编码格式
三、属性文件
Java 中的属性文件只支持iso-8859-1 编码格式,因此,要想在属性文件中保存中文,就必须使用UCS2 编码格式("uxxxx ),因此,出现了很多将这种编码转换成可视编码和工具,如Eclipse 中的一些属性文件编辑插件。
实际上,"uxxxx 编码格式在java C# 中都可以使用,如下面的语句所示:

String name= ""u7528"u6237"u540d"u4e0d"u80fd"u4e3a"u7a7a" ;
System.out.println(name);
     上面代码将输出“用户名不能为空”的信息。将 "uxxxx 格式显示成中文非常简单,那么如何将中文还原成 "uxxxxx 格式呢?下面的代码完成了这个工作:


String ss  =   " 用户名不能为空 " ;
byte [] uncode  =  ss.getBytes( " Unicode " );
int  x  =   0xff ;
String result 
= "" ;
for ( int  i =   2 ; i  <  uncode.length; i ++ )
{
    
if (i  %   2   ==   0 ) result  +=   " \\u " ;
    String abc 
=  Integer.toHexString(x  &  uncode[i]);            
    result 
+=  abc.format( " %2s " , abc).replaceAll( "   " " 0 " );               
}
System.out.println(result);
 
     上面的代码将输出如下结果:

\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a
     好了,现在可以利用这个技术来实现一个属性文件编辑器了。
四、Web 中的编码问题
     大家碰到最多的编码问题就是在 Web 应用中。先让我们看看下面的程序:

 
<!--   main.jsp   -->

  
<% @ page language = " java "   pageEncoding = " utf-8 " %>

  
< html >
      
< head >

      
</ head >

      
< body >
          
< form  action ="servlet/MyPost"  method ="post" >
              
< input  type ="text"  name ="user"   />
              
< p />
              
< input  type ="submit"   value ="提交" />
          
</ form >

      
</ body >
  
</ html >

     下面是个 Servlet

  package servlet;

  import java.io.IOException;
  import java.io.PrintWriter;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;

  
public   class  MyPost extends HttpServlet
  {

      
public   void  doPost(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException
      {
          String user 
=  request.getParameter( " user " );
          System.
out .println(user);
      }
  }

     如果中main.jsp 中输入中文后,向MyPost 提交,在控制台中会输出“ä ¸­å›½ ”,一看就是乱码。如果将IE 的当前编码设成其他的,如由utf-8 改为gbk ,仍然会出现乱码,只是乱得不一样而已。这是因为客户端提交数据时是根据浏览器当前的编码格式来提交的,如浏览器当前为gbk 编码,就以gbk 编码格式来提交。  这本身是不会出现乱码的,问题就出在Web 服务器接收数据的时候,HttpServletRequest 在将客户端传来的数据转成ucs2 上出了问题。在默认情况下,是按着iso-8859-1 编码格式来转的,而这种编码格式并不支持中文,所以也就无法正常显示中文了,解决这个问题的方法是用和客户端浏览器当前编码格式一致的编码来转换,如果是utf-8 ,则在doPost 方法中应该用以下的语句来处理:

    request.setCharacterEncoding("utf-8");
     为了对每一个Servlet 都起作用,可以将上面的语句加到filter 里。
     另外,我们一般使用象 MyEclipse 一样的 IDE 来编写 jsp 文件,这样的工具会根据 pageEncoding 属性将 jsp 文件保存成相应的编码格式,但如果要使用象记事本一样的简单的编辑器来编写 jsp 文件,如果 pageEncoding utf-8 ,而在默认时,记事本会将文件保存成 iso-8859-1 ascii )格式,但在 myeclipse 里,如果文件中有中文,它是不允许我们保存成不支持中文的编码格式的,但记事本并不认识 jsp ,因此,这时在 ie 中就无法正确显示出中文了。除非用记事本将其保存在 utf-8 格式。如下图:


 
 




 本文转自 androidguy 51CTO博客,原文链接:http://blog.51cto.com/androidguy/215325,如需转载请自行联系原作者

相关文章
|
2月前
|
安全 Java API
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
72 0
|
7天前
|
传感器 监控 数据可视化
【Java】智慧工地解决方案源码和所需关键技术
智慧工地解决方案是一种新的工程全生命周期管理理念。它通过使用各种传感器、数传终端等物联网手段获取工程施工过程信息,并上传到云平台,以保障数据安全。
30 7
|
9天前
|
存储 移动开发 Java
java核心之字符串与编码
java核心之字符串与编码
13 2
|
2月前
|
存储 NoSQL Java
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
这篇文章是关于Java面试中的分布式架构问题的笔记,包括分布式架构下的Session共享方案、RPC和RMI的理解、分布式ID生成方案、分布式锁解决方案以及分布式事务解决方案。
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
|
2月前
|
Java
Java系列之:字符串UTF-8 编码格式转换位 UTF-32 【生僻字截取问题】
这篇文章讨论了在Java中处理包含生僻字的字符串时可能遇到的问题,并提供了一种解决方法:将字符串的编码格式从UTF-8转换为UTF-32,以确保每个字符都占用固定的字节数,从而避免在截取操作中破坏字符,示例代码展示了如何进行编码转换和字符串截取。
|
2月前
|
存储 安全 Java
"Java编码魔法:揭秘图片与文件的Base64神秘转换术,让数据在指尖跳跃!"
【8月更文挑战第16天】Base64编码在Java开发中常用于将二进制数据如图片转换为ASCII字符串以便传输。编码使用64个字符及等号填充,每3字节数据编码为4个字符。Java利用`java.util.Base64`类实现此功能:读取图片或文件为字节数组后进行编码。解码时将Base64字符串还原为字节数组并写入文件。需注意编码效率降低、不提供安全性及特殊字符兼容性等问题。掌握这些技巧有助于解决Web开发中的数据传输需求。
62 4
|
2月前
|
Java 开发者
在Java编程中,if-else与switch作为核心的条件控制语句,各有千秋。if-else基于条件分支,适用于复杂逻辑;而switch则擅长处理枚举或固定选项列表,提供简洁高效的解决方案
在Java编程中,if-else与switch作为核心的条件控制语句,各有千秋。if-else基于条件分支,适用于复杂逻辑;而switch则擅长处理枚举或固定选项列表,提供简洁高效的解决方案。本文通过技术综述及示例代码,剖析两者在性能上的差异。if-else具有短路特性,但条件增多时JVM会优化提升性能;switch则利用跳转表机制,在处理大量固定选项时表现出色。通过实验对比可见,switch在重复case值处理上通常更快。尽管如此,选择时还需兼顾代码的可读性和维护性。理解这些细节有助于开发者编写出既高效又优雅的Java代码。
31 2
|
2月前
|
监控 Java
Java文件夹复制解决方案:优化大文件与大量数据的处理
Java中复制文件夹及其内容,尤其是当处理大文件或文件夹(如几个GB)时,需要特别注意内存使用和性能优化。以下是一个详细的指导,包括如何避免内存溢出异常,并确保复制过程的高效性。
|
2月前
|
Java
Java——编码GBK的不可映射字符
Java——编码GBK的不可映射字符
34 1
|
2月前
|
算法 安全 Java
探索Java中的并发编程:挑战与解决方案
【8月更文挑战第9天】 在Java世界中,并发编程是一个既令人兴奋又充满挑战的领域。它不仅为开发者提供了提高应用程序性能和响应性的机会,还带来了诸如数据一致性、线程安全和死锁等复杂问题。本文旨在通过分析Java并发的核心概念、常见并发模式及其实现方式,探讨如何在Java中有效地管理多线程环境,同时识别并解决并发编程过程中可能遇到的常见问题。
下一篇
无影云桌面