`
huxp
  • 浏览: 7138 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

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文件,如果pageEncodingutf-8,而在默认时,记事本会将文件保存成iso-8859-1ascii)格式,但在myeclipse里,如果文件中有中文,它是不允许我们保存成不支持中文的编码格式的,但记事本并不认识jsp,因此,这时在ie中就无法正确显示出中文了。除非用记事本将其保存在utf-8格式。如下图:

 

转自:http://www.blogjava.net/nokiaguy/archive/2008/07/19/216024.html

 

分享到:
评论

相关推荐

    Java架构专题,高并发架构下性能提升千倍内幕揭秘

    - **强大的社区支持**:Java拥有庞大的开发者社区,遇到问题时可以快速找到解决方案。 - **跨平台特性**:基于JVM的特性,使得Java程序可以在不同操作系统上运行,增加了应用的灵活性。 ### 二、高并发架构的关键...

    spring揭秘 完整带标签

    7. **Spring Security**:Spring Security是一个全面的安全解决方案,提供身份验证、授权等功能,保护你的应用免受攻击。 8. **Spring Cloud**:在微服务架构中,Spring Cloud提供了一套工具集,用于服务发现、配置...

    Spring揭秘.pdf

    除此之外,Spring Security提供了一套全面的安全解决方案,包括认证和授权,用于保护Spring应用免受攻击。Spring Integration则用于实现不同系统之间的集成,如文件、消息、HTTP等。 总的来说,《Spring揭秘》这...

    Java TCP/IP Socket编程

    ### Java TCP/IP Socket编程 #### 1. 计算机网络、分组报文和协议 计算机网络是指通过通信设备和线路将地理...掌握了这些知识,你就能够更好地理解如何利用Java进行网络编程,以及如何解决实际开发过程中遇到的问题。

    spring揭秘(完整).rar

    Spring Batch是Spring框架下的批处理解决方案,它为大规模数据处理提供了高级抽象和管理功能,如读取/写入策略、错误处理、事务管理等。 8. **Spring Cloud** Spring Cloud是Spring的扩展,用于构建分布式系统,...

    Java_TCPIP_Socket编程.pdf

    - **为什么需要NIO**:为了解决传统I/O模型中的阻塞问题。 - **与Buffer一起使用Channel**:如何使用Channel类进行数据传输。 - **Selector**:用于监控多个Channel的就绪状态。 - **Buffer详解** - **Buffer索引**...

    java基础知识

    通过以上知识点的学习,你可以全面掌握Java的基础知识,并能够运用这些知识来解决实际问题。无论是进一步深入学习Java高级特性还是转向其他领域如Web开发、移动开发等,这些基础知识都是不可或缺的基石。

    精通并发与 netty 视频教程(2018)视频教程

    27_gRPC双向流式数据通信详解 28_gRPC与Gradle流畅整合及问题解决的完整过程与思考 29_Gradle插件问题解决方案与Nodejs环境搭建 30_通过gRPC实现Java与Nodejs异构平台的RPC调用 31_gRPC在Nodejs领域中的静态代码生成...

    spring揭秘PDF

    8. **Spring Cloud**:针对微服务架构的一系列工具集,包括服务发现、负载均衡、断路器、配置中心等,为分布式系统提供了一整套解决方案。 9. **测试支持**:Spring提供了丰富的测试工具和库,如Spring Test、JUnit...

    ANDROID框架揭秘

    SQLite在Android框架中广泛使用,为开发者提供了便捷的数据存储解决方案。 ### Android框架的设计理念 Android框架设计遵循了模块化、可扩展性和兼容性的原则。它将复杂的系统功能分解成多个独立的服务,每个服务...

    iceiceiceice

    然而,由于提供的信息非常有限,我将假设“ICE”代表“Integration, Connectivity, and Efficiency”,这是IT行业中一个常见的抽象概念,用于描述系统集成、设备连接以及提高效率的技术或解决方案。 在IT领域,整合...

    《Android应用开发揭秘》源码4,7章

    总之,通过深入研究《Android应用开发揭秘》第四章和第七章的源码,开发者不仅可以掌握Android应用开发的基本技术,还能提升解决实际问题的能力。这不仅有助于个人技能的提升,也有助于开发出更高质量的Android应用...

    spring揭秘 经典

    它解决了EJB等传统企业级应用开发框架中存在的问题,提供了更为简洁、灵活、高效的解决方案,成为现代企业级应用开发的首选框架。Spring框架的成功不仅改变了Java EE的开发格局,也为整个软件开发行业带来了深远的...

    Android应用源码之应用开发揭秘源码 .rar

    在Android应用开发的世界里,源码是学习和理解应用程序运行机制的关键。...对于经验丰富的开发者,它可以提供新的视角和解决问题的方法。总之,这个源码包是一个宝贵的教育资源,值得花时间去深入研究。

    JavaServer Faces完全参考手册(JSF:Complete Reference)

    - **Jakarta Struts**:作为一种流行的MVC框架,Jakarta Struts(后来的Apache Struts)通过提供控制器层来解决上述问题,但它依然存在不足之处。 - **JSF 的诞生**:为了克服这些问题并提供更加强大的Web应用程序...

    JavaSIG-Puzzlers.pdf

    ##### 解决方案 为了修复这个问题,可以采用以下两种方式之一: 1. **显式类型转换**: ```java if (b == (byte)0x90) { System.out.println("Joy!"); } ``` 2. **使用位掩码**: ```java if ((b & 0xff) ...

    四平方和_四平方和_

    5. **应用示例**:可能包含四平方和在实际问题中的应用,比如在密码学中的应用,或者在解决其他数学问题时作为辅助工具的示例。 6. **教学材料**:如果这是一个教育相关的资源,那么可能会有详细的教程、课件或习题...

Global site tag (gtag.js) - Google Analytics