一、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 解析就是“涓   浗 ”。如下图所示。

Java编码问题解决方案大揭密_第1张图片

如果使用下面的语言可以获得“中国”的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 应用中。先让我们看看下面的程序:

 


  
<% @ 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 格式。如下图:

Java编码问题解决方案大揭密_第2张图片