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

简介: 本文为原创,如需转载,请注明作者和出处,谢谢! 一、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编码中为不同国家的语言进行了分页,这个分页也叫“代码页”或“编码页”。中文根据包含中文字符的多少,分了很多代码页,如cp935cp936等,然而,这些都是在UCS2中的代码页名,而对于操作系统来说,如微软的windows,一开始的中文编码为GB2312,后来扩展成了GBK。其实GBKcp936是完全等效的,用它们哪个都行。

二、Java编码转换

   
上面说了这么多,在这一部分我们做一些编码转换,看看会发生什么事情。

    先定义一个字符串变量:

    String gbk = "
中国"; // “中国”在Java内部是以UCS2格式保存的

    用下面的语言输出一定会输出中文:

System.out.println(gbk);

    实现上,当我们从IDE输入“中国”时,用的是java源代码文件保存的格式,一般是GBK,有时也可是utf-8,而在Java编译程序时,会不由分说地将所有的编码格式转换成utf-8编码,读者可以用UltraEdit或其他的二进制编辑器打开上面的“中国.class”,看看所生成的二进制是否有utf-8的编码(utf-8ucs2之间的转换非常容易,因为utf-8ucs2之间是用公式进行转换的,而不是到代码页去查,这就相当于将二进制转成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编码为2D4EGBK编码为D6D0

    读者可访问如下的url自行查验:

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

    当然,感兴趣的读者也可以试试其他语言的编码,如“人类”的韩语是“인간의”,如下面的代码将输出“인간의”的cp949ucs2编码,其中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编码为C778cp949的编码为C0CE,要注意的是,在cp949中,ucs2编码也有C0CE,不要弄混了。读者可以访问下面的url来验证:

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

Java支持的编码格式

三、属性文件

Java中的属性文件只支持iso-8859-1编码格式,因此,要想在属性文件中保存中文,就必须使用UCS2编码格式("uxxxx),因此,出现了很多将这种编码转换成可视编码和工具,如Eclipse中的一些属性文件编辑插件。

实际上,"uxxxx编码格式在javaC#中都可以使用,如下面的语句所示:

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


 

 



国内最棒的Google Android技术社区(eoeandroid),欢迎访问!

《银河系列原创教程》发布

《Java Web开发速学宝典》出版,欢迎定购

目录
相关文章
|
2月前
|
Java
Java开发实现图片URL地址检验,如何编码?
【10月更文挑战第14天】Java开发实现图片URL地址检验,如何编码?
79 4
|
2月前
|
关系型数据库 MySQL Java
【IDEA】java后台操作mysql数据库驱动常见错误解决方案
【IDEA】java后台操作mysql数据库驱动常见错误解决方案
79 0
|
2月前
|
Java
Java实现随机生成某个省某个市的身份证号?如何编码?
【10月更文挑战第18天】Java实现随机生成某个省某个市的身份证号?如何编码?
130 5
|
2月前
|
Java
Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
【10月更文挑战第14天】Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
64 2
|
4月前
|
安全 Java API
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
121 0
|
17天前
|
安全 Java 开发者
Java多线程编程中的常见问题与解决方案
本文深入探讨了Java多线程编程中常见的问题,包括线程安全问题、死锁、竞态条件等,并提供了相应的解决策略。文章首先介绍了多线程的基础知识,随后详细分析了每个问题的产生原因和典型场景,最后提出了实用的解决方案,旨在帮助开发者提高多线程程序的稳定性和性能。
|
22天前
|
人工智能 监控 数据可视化
Java智慧工地信息管理平台源码 智慧工地信息化解决方案SaaS源码 支持二次开发
智慧工地系统是依托物联网、互联网、AI、可视化建立的大数据管理平台,是一种全新的管理模式,能够实现劳务管理、安全施工、绿色施工的智能化和互联网化。围绕施工现场管理的人、机、料、法、环五大维度,以及施工过程管理的进度、质量、安全三大体系为基础应用,实现全面高效的工程管理需求,满足工地多角色、多视角的有效监管,实现工程建设管理的降本增效,为监管平台提供数据支撑。
34 3
|
29天前
|
Java API Apache
|
2月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
84 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
2月前
|
Java
短频快task的java解决方案
本文探讨了Java自带WorkStealingPool的缺陷,特别是在任务中断方面的不足。普通线程池在处理短频快任务时存在锁竞争问题,导致性能损耗。文章提出了一种基于任务窃取机制的优化方案,通过设计合理的窃取逻辑和减少性能损耗,实现了任务的高效执行和资源的充分利用。最后总结了不同场景下应选择的线程池类型。