`
p2world
  • 浏览: 95865 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java 中文 编码 问题 感悟

    博客分类:
  • java
阅读更多
String newStr = new String(oldStr.getBytes(), "UTF-8");    
 
java中的String类是按照unicode进行编码的,当使用String(byte[] bytes, String encoding)构造字符串时,encoding所指的是bytes中的数据是按照那种方式编码的,而不是最后产生的String是什么编码方式,换句话说,是让系统把bytes中的数据由encoding编码方式转换成unicode编码。如果不指明,bytes的编码方式将由jdk根据操作系统决定。   
 
         当我们从文件中读数据时,最好使用InputStream方式,然后采用String(byte[] bytes, String encoding)指明文件谋嗦敕绞健2灰褂肦eader方式,因为Reader方式会自动根据jdk指明的编码方式把文件内容转换成unicode 编码。   
 
         当我们从数据库中读文本数据时,采用ResultSet.getBytes()方法取得字节数组,同样采用带编码方式的字符串构造方法即可。   
 
ResultSet rs;   
bytep[] bytes = rs.getBytes();   
String str = new String(bytes, "gb2312");   
 
不要采取下面的步骤。   
 
ResultSet rs;   
String str = rs.getString();   
str = new String(str.getBytes("iso8859-1"), "gb2312");   
 
         这种编码转换方式效率底。之所以这么做的原因是,ResultSet在getString()方法执行时,默认数据库里的数据编码方式为 iso8859-1。系统会把数据依照iso8859-1的编码方式转换成unicode。使用str.getBytes("iso8859-1")把数据还原,然后利用new String(bytes, "gb2312")把数据从gb2312转换成unicode,中间多了好多步骤。   
 
         从HttpRequest中读参数时,利用reqeust.setCharacterEncoding()方法设置编码方式,读出的内容就是正确的了。 

先说Java。    
   JVM里面的任何字符串资源都是Unicode,就是说,任何String类型的数据都是Unicode编码。没有例外。既然只有一种编码,那么,我们可以这么说,JVM里面的String是不带编码的。String相当于    char[]。    
   JVM里面的    byte[]    数据是带编码的。比如,Big5,GBK,GB2312,UTF-8之类的。    
   一个GBK编码的byte[]    转换成    String,其实就是从GBK编码向Unicode编码转换。    
   一个String转换成一个Big5编码的byte[],其实就是从Unicode编码向Big5编码转换。    
   所以,Unicode是所有编码转换的中间介质。所有的编码都有一个转换器可以转换到Unicode,而Unicode也可以转换到其他所有的编码。这样构成了一个总线结构。    
   比如,如果总共有10种编码,那么只需要    10    +    10    =    20个转换器就够了。如果要是两两直接转换,那么,需要的转换器数量是一个组合数字,需要90个转换器。  
   
   一个系统的不同部分,都有自己的编码。比如,数据库,文件,JVM,浏览器这4个部分。    
   在这些部分之间数据交换的地方,就会出现编码问题。比如,数据库和JVM之间,文件和JVM之间,浏览器和JVM之间。这些问题的原理都是相通的。  
   
   编码问题最容易处理的地方是文件和JVM之间。文件IO    API带有encoding    参数,请自行查阅。    
   最不容易出现编码问题的地方是数据库和JVM之间。这应该是数据库JDBC连接的基本功能。本文不专门进行讨论。    
   最容易出问题的地方是浏览器和服务器JVM之间(其实,代码里面的字符串更容易出问题,不过,我已经事先声明,本文不讨论代码中的字符串编码)。下面主要讨论这块浏览器和服务器JVM之间的编码问题。  
   
   我们把浏览器编码叫做    Browser_Charset,把JVM编码叫做JVM_Charset(通常等于服务器系统编码)。    
   当浏览器的数据过来的时候,是一个带有Browser_Charset的byte[]。    
   如果用户处理程序需要一个String类型的数据,那么JVM会好心好意地把这个byte[]转换成String。使用的转换器是    JVM_Charset    ->    Unicode。    
   注意,如果这个时候,Browser_Charset    和    JVM_Charset并不相等。那么,这个自动转换是错误的。    
   为了弥补这个错误。我们需要做两步工作。    
   (1)    Unicode    ->    JVM_Charset,把这个String    转换回到原来的    byte[]。    
   (2)    Browser_Charset    ->    Unicode,把这个还原的byte[]转换成    String。  
   
   这个效果,和直接从HTTP    Request取得byte[],然后执行    (2)    Browser_Charset    ->    Unicode    的效果是一样的。  
   
   如果在Request里面设置了CharacterEncoding,那么POST    Data参数就不需要自己手工转换了,web    server的自动转换就是正确的。URL的参数编码还涉及到URL编码,需要考虑的问题多一些,没有这么简单。 
   
   JVM把数据发到浏览器的时候。也需要考虑编码问题。可以在Response里面设置。另外,HTML    Meta    Header里面也可以设置编码,提醒Browser选择正确编码。  
   
   有些语言的VM或者解释器的字符串编码可能不同。比如,Ruby。不过,编码转换原理都是一样的。  
   
   That    is    all.  
   

JAVA字符编码    
     
一、概要  
在JAVA应用程序特别是基于WEB的程序中,经常遇到字符的编码问题。为了防止出现乱码,首先需要了解JAVA是如何处理字符的,这样就可以有目的地在输入/输出环节中增加必要的转码。其次,由于各种服务器有不同的处理方式,还需要多做试验,确保使用中不出现乱码。  
二、基本概念  
2.1    JAVA中字符的表达  
JAVA    中有char、byte、String这几个概念。char    指的是一个UNICODE字符,为16位的整数。byte    是字节,字符串在网络传输或存储前需要转换为byte数组。在从网络接收或从存储设备读取后需要将byte数组转换成String。String是字符串,可以看成是由char组成的数组。String    和    char    为内存形式,byte是网络传输或存储的序列化形式。  
举例:  
英  
String    ying    =    “英”;  
char    ying    =    ying.charAt(0);  
String    yingHex    =    Integer.toHexString(ying);  
82    F1    
byte    yingGBBytes    =    ying.getBytes(“GBK”);  
GB编码的字节数值  
D3    A2    
2.2    编码方式的简介  
String序列化成byte数组或反序列化时需要选择正确的编码方式。如果编码方式不正确,就会得到一些0x3F的值。常用的字符编码方式有ISO8859_1、GB2312、GBK、UTF-8/UTF-16/UTF-32。  
ISO8859_1用来编码拉丁文,它由单字节(0-255)组成。  
GB2312、GBK用来编码简体中文,它有单字节和双字节混合组成。最高位为1的字节和下一个字节构成一个汉字,最高位为0的字节是ASCII码。  
UTF-8/UTF-16/UTF-32是国际标准UNICODE的编码方式。    用得最多的是UTF-8,主要是因为它在对拉丁文编码时节约空间。  
UNICODE值    UTF-8编码  
U-00000000    -    U-0000007F:    0xxxxxxx  
U-00000080    -    U-000007FF:    110xxxxx    10xxxxxx    
U-00000800    -    U-0000FFFF:    1110xxxx    10xxxxxx    10xxxxxx    
U-00010000    -    U-001FFFFF:    11110xxx    10xxxxxx    10xxxxxx    10xxxxxx    
U-00200000    -    U-03FFFFFF:    111110xx    10xxxxxx    10xxxxxx    10xxxxxx    10xxxxxx    
U-04000000    -    U-7FFFFFFF:    1111110x    10xxxxxx    10xxxxxx    10xxxxxx    10xxxxxx    10xxxxxx    
三、J2SE中相关的函数  
String    str    =”英”;  
//取得GB2312编码的字节  
byte[]    bytesGB2312    =    str.getBytes(“GB2312”);    
//取得平台缺省编码的字节(solaris为ISO8859_1,windows为GB2312)  
byte[]    bytesDefault    =    str.getBytes();  
//用指定的编码将字节转换成字符串  
String    newStrGB    =    new    String(bytesGB2312,    “GB2312”);  
   
//用平台缺省的编码将字节转换成字符串(solaris为ISO8859_1,windows为GB2312)  
String    newStrDefault    =    new    String(bytesDefault);  
//用指定的编码从字节流里面读取字符  
InputStream    in    =    xxx;  
InputStreamReader    reader    =    InputStreamReader(    in,    “GB2312”);  
char    aChar    =    reader.read();  
四、JSP、数据库的编码  
4.1    JSP中的编码  
(1)    静态声明:  
CHARSET有两个作用:  
JSP文件的编码方式:在读取JSP文件、生成JAVA类时,源JSP文件中汉字的编码  
JSP输出流的编码方式:在执行JSP时,往response流里面写入数据的编码方式  
(2)    动态改变:在往response流里面写数据前可以调用response.setContentType(),设定正确的编码类型。  
(3)    在TOMCAT中,由Request.getParameter()    得到的参数,编码方式都是ISO8859_1。所以如果在浏览器输入框内输入一个汉字“英”,在服务器端就得到一个ISO8859_1编码的(0x00,0xD3,0x00,0xA2)。所以通常在接收参数时转码:  
String    wrongStr    =    response.getParameter(“name”);  
String    correctStr    =    new    String(wrongStr.getBytes(“ISO8859_1”),”GB2312”);  
在最新的SERVLET规范里面,也可以在获取参数之前执行如下代码:  
request.setCharacterEncoding(“GB2312”);  
4.2    数据库的编码  
(1)    数据库使用UTF-16  
如果String中是UNICODE字符,写入读出时不需要转码  
(2)    数据库使用ISO8859_1  
如果String中是UNICODE字符,写入读出时需要转码  
写入:String    newStr    =    new    String(oldStr.getByte(“GB2312”),    “ISO8859_1”);  
读出:String    newStr    =    new    String(oldStr.getByte(“ISO8859_1”),”GB2312”);  
五、源文件的编码  
5.1    资源文件  
资源文件的编码方式和编辑平台相关。在WINDOWS平台下编写的资源文件,以GB2312方式编码。在编译时需要转码,以确保在各个平台上的正确性:  
native2ascii    ?encoding    GB2312    source.properties  
这样从资源文件中读出的就是正确的UNICODE字符串。  
5.2    源文件  
源文件的编码方式和编辑平台相关。在WINDOWS平台下开发的源文件,以GB2312方式编码。在编译的时候,需要指定源文件的编码方式:  
javac    ?encoding    GB2312  
JAVA编译后生成的字节文件的编码为UTF-8。  
   
①最新版TOMCAT4.1.18支持request.setCharacterEncoding(String    enc)  
②资源文件转码成company.name=u82f1u65afu514b  
③如果数据库使用utf-16则不需要这部分转码  
④页面上应有  
转码?:  
String    s    =    new    String  
(request.getParameter(“name”).getBytes(“ISO8859_1”),”GB2312”);  
转码?:  
String    s    =    new    String(name.getBytes(“GB2312”),”ISO8859_1”);  
转码?:  
String    s    =    new    String(name.getBytes(“ISO8859_1”),”    GB2312”);  
   
     
   
     =========================================================  
   
JAVA内部究竟是用的什么字符编码呢?这个问题我也找了很久,后来在THINK    IN    JAVA    3rd的12章里看到一个例子出现了UTF-16BE,难道是它?    
             byte[]    utf_16be    =    name.getBytes("utf-16be");  
   
             printbyte(utf_16be);  
   
结果出来了:58    02      length    =    2  
哈哈,I    got    it!不多不少两个字节,内容也一样。果然是它。同时我在里面也看到,UNICODE的编码还有一个LE,这里的BE,LE我想应该是bigendian和littleendian吧。  
   
     ==========================================================  
   
import    java.io.*;  
             public    class    TestCodeIO    {  
                         public    static    void    main(String[]    args)    throws    Exception{  
                                     InputStreamReader    isr    =    new    InputStreamReader(System.in,"iso8859-1");  
                                                 //Create    an    InputStreamReader    that    uses    the    given    charset    decoder  
                                     BufferedReader    br    =    new    BufferedReader    (isr);  
                                     String    strLine    =    br.readLine();  
                                     br.close();  
                                     isr.close();  
                                     System.out.println(strLine);  
                                     System.out.println(new    String    (strLine.getBytes(),"iso8859-1"));//错误改法  
                                                 //Encodes    this    String    (strLine)    into    a    sequence    of    bytes    using    the    platform's    
                                                 //default    charset(gb2312)    then    constructs    a    new    String    by    decoding    the    
                                               //specified    array    of    bytes    using    the    specified    charset    (iso8859-1)  
                                               //because    this    String    (strLine)    uses    the    charset    decoder    "iso8859-1",so    it    can  
                                               //only    be    encoded    by    "iso8859-1",cann't    be    encoded    by    the    platform's    default  
                                               //charset    "gb2312",so    this    line    is    wrong.  
                                   System.out.println(new    String    (strLine.getBytes("iso8859-1")));//正确改法  
                                             //Encodes    this    String    (strLine)    into    a    sequence    of    bytes    using    the    named    
                                             //charset    (iso8859-1),then    constructs    a    new    String    by    decoding    the    
                                             //specified    array    of    bytes    using    the    platform's    default    charset    (gb2312).  
                                             //This    line    is    right.  
                 }    
}  
   
上面的英文注释已经说得很清楚了,这里我还是解释一下吧:  
   
首先是错误的改法      System.out.println(new    String    (strLine.getBytes(),"iso8859-1"));  
这句代码是将strLine中的字符串用系统默认的编码方式(这里是gb2312)  
转换为字节序列,然后用指定的编码方式(这里是iso8859-1)构造一个新的  
String对象,并打印到屏幕上。  
错误在哪里呢?  
   
请注意这一段代码      
InputStreamReader    isr    =    new    InputStreamReader(System.in,"iso8859-1");  
BufferedReader    br    =    new    BufferedReader    (isr);  
String    strLine    =    br.readLine();  
这里strLine存储的内容是用指定的编码方式(iso8859-1)存储的,而转换成字节码  
的时候(这句代码strLine.getBytes())却使用了系统默认的gb2312编码,所以当然就  
输出乱码了!然后用gb2312编码的字节序列构建新的String对象的时候又使用了  
iso8859-1编码,所以输出的乱码和System.out.println(strLine)有所不同。  
   
至于正确的改法就不用详细说明了吧,首先将strLine用iso8859-1编码方式转换成字节  
序列,然后用系统默认的编码方式(gb2312)构建新的String对象,然后打印输出。 
0
1
分享到:
评论

相关推荐

    进击的Java 第16期

    从Java语言本身的优势讨论到在Android平台上的高效编码实践,再到Java8的新特性及函数式编程的应用,以及Java程序员如何转型为其他领域的开发者,本期内容覆盖广泛,深入浅出。 #### Java太笨?纯粹诽谤 - **背景...

    初级java实训机票预订系统为了完成老师的作业

    初级Java实训机票预订系统项目,正是基于这一教育背景而设计的,旨在帮助学生通过实际操作来深化对Java编程语言的理解和应用,同时让学生体验到软件开发从设计到实现的全流程。 项目标题“初级Java实训机票预订系统...

    S1JAVA总结

    在此过程中,不仅克服了学习初期的种种困难,也积累了一些有效的学习方法和个人感悟。下面,我将详细分享在JAVA学习旅程中的收获与体会。 ### 1. 学习方法 #### 1.1 多打代码 实践证明,多打代码是提高编程技能的...

    Java课程设计报告——计算器

    这份课程设计报告提供了学生赵亮在Java编程实践中所学的关键知识点,展示了从需求分析、设计、编码到测试的完整软件开发流程。通过这样的项目,赵亮无疑增强了编程能力,加深了对Java语言特性和软件工程实践的理解。

    写在感悟分享之前(csdn)————程序.pdf

    由于当时没有人会Java,所以大家都是一边上网查资料一边编码。作者发现SUN网站上的资料基本都是英文的,读起来效率较低,于是CSDN网站成了他们的首选。从那以后,上CSDN网站查资料也成了工作的一部分,这种状态大约...

    我的编程感悟(中文PDF)(共37M二分卷)分卷二

    7.2.4 MMX 编码技巧 197 7.2.5 SIMD 的应用举例 201 7.2.6 SIMD编程注意要点 203 7.2.7 SIMD的性能检测 204 第8章 引擎中的优化 205 8.1 Blit的优化 207 8.2 脏矩形优化 209 8.2.1 “风魂”中改进的脏矩形算法 215 ...

    《Java夜未眠.程序员的心声》.rar

    虽然我们无法详细阅读压缩包内的内容,但从标题和文件名我们可以推测这本书籍可能涵盖了以下几个方面的Java编程知识和程序员生活感悟: 1. **Java基础知识**:作为一本关于Java的书籍,它很可能会涵盖Java的基础...

    java实习心得体会8篇.docx

    【描述】:本文主要分享了作者在Java实习过程中的体验和感悟,强调了项目实践对于技能提升的重要性,并结合物联网技术的应用,探讨了如何选择合适的实习项目和如何确保实习效果。 【标签】:物联网 【部分内容】:...

    Java博客概要设计文档

    ### Java博客概要设计文档 #### 一、项目概述与技术栈 本项目是一个基于Spring Cloud全家桶开发的微服务架构的博客系统。系统利用了服务注册与发现、断路器、配置中心等功能来构建一个高可用、可扩展的平台。 - *...

    Java程序员+上班那点事儿

    《Java程序员+上班那点事儿》这本书主要面向的是正在或者准备进入Java开发领域的读者,它以生动的故事和实例,深入浅出地讲述了Java程序员在工作中可能遇到的各种问题和解决之道。书中涵盖了技术学习、项目实战、...

    2022年java实习工作总结优质.docx

    这篇文档似乎是一份学生对2022年Java实习经历的总结,主要涉及了实习过程中的学习体验、技能提升和个人感悟。以下是对这些内容的详细阐述: 1. **Java知识深化**: 实习期间,学生意识到自己在Java编程方面的基础...

    BLOG(Final).rar_JSP博客系统_blog_blog java_java blog_myeclipse

    通过这个系统,用户可以方便地在线创作和分享自己的思想与感悟。这里我们将深入探讨该系统的构成、核心技术及实现原理。 **一、JSP与Servlet技术** JSP(JavaServer Pages)是Java平台上的动态网页技术,它允许...

    2022年java毕业实习报告.docx

    【Java 毕业实习报告】是对高校生在学习Java编程和相关技术后,通过实际项目实习进行知识应用和技能提升的总结。这份报告详细记录了实习的目的、内容、过程和收获,旨在评估学生的实际操作能力和对软件开发流程的...

    Java仓库管理系统报告.doc

    【Java仓库管理系统报告.doc】是一份关于面向对象程序设计(Java)课程的期末实践考核项目,旨在考察学生对Java编程语言、面向对象编程思想以及软件工程流程的理解与应用能力。项目选择开发一个仓库管理系统,该系统...

    java毕业实习报告范文3篇[Word稿].doc

    - 实习报告应详尽记录项目过程,包括遇到的问题、解决方案以及个人感悟,以反映技能成长。 - 完成项目后,通过自我评估和导师反馈,评估技能掌握程度,为后续学习提供方向。 总的来说,一份优秀的 Java 毕业实习...

    我的编程感悟(中文PDF)(共37M二分卷)分卷一

    7.2.4 MMX 编码技巧 197 7.2.5 SIMD 的应用举例 201 7.2.6 SIMD编程注意要点 203 7.2.7 SIMD的性能检测 204 第8章 引擎中的优化 205 8.1 Blit的优化 207 8.2 脏矩形优化 209 8.2.1 “风魂”中改进的脏矩形算法 215 ...

    毕业论文java vue springboot mysql 致远汽车租赁系统.docx

    该系统旨在提升致远汽车租赁公司的运营效率,通过细致的需求分析、系统设计、编码实现和测试,实现了对汽车租赁业务的有效管理。 在需求分析阶段,论文首先明确了系统的研究内容,这包括对业务需求的理解,如租车...

    java课程设计资料报告材料-俄罗斯方块.doc

    - **心得体会**:设计者通常会分享在项目中遇到的挑战、解决问题的过程以及对Java编程和游戏开发更深的理解和感悟。 通过这个课程设计,学生不仅学习了Java编程语言,还了解了软件开发的流程,包括需求分析、设计...

    毕业论文java vue springboot mysql 生鲜交易系统.docx

    9. 实现与测试,记录编码过程和系统测试结果; 10. 总结,对论文和项目开发的回顾与感悟; 11. 致谢,感谢给予帮助和支持的人员; 12. 参考文献,列出在研究过程中引用的相关资料。 2. 开发技术介绍 本文将详细...

Global site tag (gtag.js) - Google Analytics