java request乱码怎样解决复杂文字的乱码?

  一、表单提交时出现乱码:
  在进行表单提交的时候,经常提交一些中文,自然就避免不了出现中文乱码的情况,对于表单来说有两种提交方式:get和post提交方式。所以请求的时候便有get请求和post请求。以前我一直以为get请求和post请求方式出现的乱码的解决方式是一样的,但是今天才知道两种请求方式所产生的乱码的解决方式是不同的。每种方式都有着不同的解决方法,之所以出现乱码,原因就在于get请求时,其传递给服务器的数据是附加在URL地址之后的;而post的请求时,其传递给服务器的数据是作为请求体的一部分传递给服务器。这也就导致了对它们所产生的乱码的处理方式是不同的。
  1、客户端的get请求
  对于不同的请求方式,解决乱码的问题也是不一样的,对于客户端的get请求来说,服务器端处理要想不出现乱码,解决这个问题稍微复杂一些,需要用到String类型的构造函数,其中的一个构造函数就是用指定的编码方式去解码,一般都用“UTF-8”的方式。只要在服务器端将请求得到的参数重新构造成一个字符串就行了。如下所示:
  String stuname = request.getParameter("stuname");
  String str = new String(stuname.getBytes("ISO-8859-1"),"utf-8")
  经过构造之后,客户端输入中文,且表单时get请求的情况下,str就变成了中文了。如果请求参数比较多,最好将它封装成一个工具类:
  public class MyUtil
  public static String getNewString(String str) throws UnsupportedEncodingException
  return new String(str.getBytes("ISO-8859-1"),"UTF-8");
  String stuname= MyUtil.getNewString(request.getParameter("stuname"));
  2、客户端的post请求
  对于客户端的post请求来说,处理乱码的问题就比较简单了,因为请求的数据时作为请求体的一部分传递给服务器的,所以只要修改请求内的编码就行了。只要在服务器端的最开始处将请求的数据设置为“UTF-8”就行了,输入如下语句:
  request. setCharacterEncoding(“UTF-8”);
  这样用户在服务器端获取到的中文数据就不再是乱码了。
  二、超链接时出现乱码(低版本浏览器不行IE6)
  在Web开发中,挺多的时候都是通过超链接去传递中文参数的,这也会导致在显示的时候也会出现乱码,对于超链接来说,它实际上是向服务器端发送了一个请求,而它发出的请求是属于get请求,所以对于超链接的乱码来说,它处理乱码的方式和表单的get请求出现乱码的方式是一样的。
  String stuname= MyUtil.getNewString(request.getParameter("stuname"));
  三、重定向时出现乱码(低版本浏览器不行IE6)
  有时写上response的sendRedirect()方法进行重定向时也会出现乱码,重定向时实际上也是向服务器发送了一个请求,所以解决乱码的方法和和上面是一样的。
  四、浏览器版本低导致的乱码
  上网的时候,有时提交的一些信息在地址栏显示的是“%2C%C6%CC%C6”的字样,其实这都是防止出现乱码进行的解决方案,如果你的浏览器是IE6或以下版本,则我们的第二种情况和第三种情况会出现乱码(尤其是当中文是奇数的时候),这就不好使了所以我们必须采用另一种比较实际的作法:
  在java.net包中提供了URLEncoder类和URLDcoder类,这两个类又分别提供了encode和decode两个静态方法,分别用于进行编码和解码。我们将要传递的中文参数进行编码之后,在传递给服务器,服务器解码之后,就可以显示中文了。
  进行编码:URLEncoder.encode(stuname,”UTF-8”)
  传递给服务器:&a href=”/1.jsp?stuname&%=stuname%&”&传递&/a&
  进行解码:URLDecoder.decode(stuname,”UTF-8”);
  这样就可以得到传递过来的中文参数了,我发现许多网站用的都是这种方式解决中参数的。
  五、返回浏览器显示的乱码
  在Servlet编程中,经常需要通过response对象将一些信息返回给浏览器,给我们的客户端,而我们在服务器端显示的中文,但是响应给客户端浏览器却是乱码,这主要是由于response对象的getWriter()方法返回的PrintWriter对象默认使用“ISO-8859-1”字符集编码进行Unicode字符串到字节数组的转换,由于ISO8859-1字符集中根本就没有包含中文字符,所以Java在进行转换的时候会将无效的字符编码输出给客户端,于是便出现了乱码,为此ServletResponse接口中便定义了setCharacterEncoding、setContentType等方法来指定getWriter方法返回的PrintWriter对象所使用的字符集编码,所以我们在写Servlet程序中,在调用getWriter方法之前设置这些方法的值。我们为了防止乱码,经常将以下两条语句一起写上:
  response.setContentType(“text/charset=utf-8”);
  response. setCharacterEncoding(“UTF-8”);
  只要编写Servlet文件中含有响应给客户端的信息,那么就要写上这两句话。最好写上第二句话,因为它的优先级高,它的设置结果将覆盖setContentType等方法设置的字符编码集。
  六、修改Tomcat的编码
  在上述的get请求所导致乱码问题中,还有一种解决的方案,我们常用Tomcat作为运行Servlet和JSP的容器,而Tomcat内部默认的编码是ISO-8859-1,所以对于get请求方式,其传递的数据(URI)会附加在访问的资源后面,其编码是Tomcat默认的,如果修改该URI的编码,那么对于所有的get请求方式便不会出现乱码了包括上边说的重定向和超链接,在Tomcat的配置文件server.xml中找到修改Tomcat的端口的地方,在其内部加入URIEncoding属性,设置为和你的项目中所设的编码一样的值,这里全部都是UTF-8。如下所示:
  &Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"redirectPort="8443" URIEncoding="UTF-8" /&
  在编写Servlet和JSP的时候,为了避免出现乱码,最重要的就是:采用一致的编码,如果编码都一致了,肯定不会出现乱码。
【】【】【】【】
ISBN编号:&8
出版时间:&2013-3
出版社:&中国人事出版社
定价:¥45 优惠价:¥45&&ISBN编号:&9
出版时间:&2013-4
出版社:&中国人事出版社
定价:¥45 优惠价:¥45&&
????????????
????????????
         Copyright ©
() All Rights Reserved1819人阅读
String&odsStr&=&&测试&;
String&newStr&=&new&String(odsStr.getBytes(&GBK&),&&ISO8859_1&);
首先需要说明一下我们经常用到的字符集,有ISO8859-1,GB2312,GBK,GB18030,UNICODE。这里ISO8859-1字符集只 包含英文字符,使用一个字节存储。GB2312、GBK和GB18030字符集包含中文字符,他们都兼容ISO8859-1字符集,他们的字符存储格式是变长的,其中GB18030包含GBK,GBK包含GB2312。UNICODE包含世界上所有国家的字符,UNICODE又分为UTF-8,UTF-16和UTF-32三种,UTF-8是变长字符集,它兼容ISO8859-1,即英文字符使用一个字节编码,而其他的字符使用2到4个字节编码,其中中文字符大部分都是使用3个字节进行编码,少量偏僻字使用4个字节编码,UTF-16统一都使用2个字节编码,它不兼容ISO8859-1,英文字符也使用两个字节,UTF-32统一使用4个字节编码,也不兼容ISO8859-1,可见UTF-16和UTF-32都比较浪费空间。
乱码问题的产生最根本的原因就是使用错误的字符集解码字节流或者将给定的字符串用错误的字符集编码成错误字节流造成的,例如”中文”两个汉字,如果用ISO8859-1字符集将其编码为字节流,因为这个字符集不支持中文,所以就会出错,输出结果为3f3f,其意义就是??。再例如”中文”二字的GBK的字节流为d6 d0 ce c4,可是我们要是用不兼容的字符集去解码,例如用ISO8859-1或者UTF-8,这随后产生的字符串就是乱码,或者是其他的某个字符。
从开发Java程序到运行Java程序的过程中都存在着编码问题,所以要想避免乱码产生,就必须了解在其中任何时候的编码处理的情况。
源代码:在编写java源代码的时候,我们必须把编写的文本保存在文件中,这个时候不管用什么编辑器,都存在一个问题,就是以什么样的字符集将这些源代码(包含汉字)保存到文件中,大部分编辑器都会通过系统的环境变量得到系统的当前默认字符集,编辑器就会使用这个字符集将我们编写的源代码保存到文件中。一般我们的中文Windows系统的默认字符集是GB18030,AIX英文环境的默认字符集是ISO8859-1,AIX中文环境的默认字符集是IBM-eucCN。
编译:在编译.java文件的时候如果使用默认处理,则javac会使用系统当前的默认字符集去读取源文件,将源文件的内容转换为UTF-8编码,然后在进行编译,这时我们也可以通过-encoding参数指定一个字符集,让javac使用我们指定的字符集去读取源代码然后在转换为UTF-8,然后编译。编译以后产生的class文件内部所有的中文字符都是用UTF-8的字符集进行编码的,这就是Java程序能处理任何国家文字的原因。
运行时:Java程序在运行时,需要使用程序内部定义的中文字符串,也可能会使用从外部读取的中文字符串,这些经过处理,可能都会输出到程序外部,在这些 过程中都涉及到编码的转换,程序内部定义的字符串都是用UTF-8存储的。而从外部读取和输出到程序外部的输出又使用什么字符集进行处理呢?在我们没有在 程序中特别指定的情况下,JVM会根据系统属性确定使用哪个字符集,这个系统属性的名称为file.encoding,我们可以在启动java程序的时候通过-D参数设定这个值,如果没有设定,JVM会根据系统环境变量确定这个系统属性,一般我们的中文Windows系统的默认字符集是GB18030,AIX英文环境的默认字符集是ISO8859-1,AIX中文环境的默认字符集是IBM-eucCN。这样JVM在处理输入数据的时候就会把字节流根据这个参数进行解码,然后转成UTF-8格式,在Java程序内部处理,然后再根据这个参数把处理后的数据编码,输出到程序外部。这就是Java程序运行时字符集的使用情况。
现在有一个问题,我们平时都是Windows的中文环境下做开发,然后拿到AIX系统上去运行,AIX系统的默认语言环境是英文环境,这样就会出现乱码,分析过程如下:源文件编码格式为GB18030,默认编译,也采用GB18030读取源文件,正常转换为UTF-8,生成class文件,运行时没有进行特殊设置,语言环境为英文环境,默认编码为ISO8859-1,这样在输出中文的时候会把正常的UTF-8表示的汉字用ISO8859-1的字符集去编码生成字节流,因为ISO8859-1不支持汉字,结果输出的都是’?’。可是这个时候却发现,由外界输入给java程序的中文字符,却能正常输出,这又是为什么,其实这个也是运行时的默认字符集ISO8859-1造成的,Java程序运行时,在读取外部进入的字节流的时候,如果使用默认的读取方式,也是使用ISO8859-1的字符集进行解码处理,这样中间的处理过程中,中文都已经不是原来的中文了,也就是说我们这个时候处理根本不是我们认为的中文,而是一对乱码,虽然是乱码,但是其中的信息却没有丢失,在处理完后,在经过一次ISO8859-1的编码,又还原为正常的GB18030的编码输出,所有没有出现乱码。我们以前的解决方法是,在编译原文件的时候指定参数-encoding
ISO8859-1,让编译器用ISO8859-1的字符集去解码源文件编译,然后运行程序,这时再输出程序的内部中文字符串也不是乱码了。看起来一切都解决了,可是却没有从根本上解决问题,class文件变得比平常大很多,程序中用到中文越多,class文件变大的越快。而且其中的中文信息也变味了。
另一个问题,如果我们正常编译程序,在AIX系统上线设定为中文环境,然后再运行Java程序,这样既不会使程序变大,也不会使中文变味,可是用了一段时间又发现问题了,处理过程中如果遇到偏僻的中文字,还是乱码,原因是AIX的中文环境使用的字符集是IBM-eucCN,我认为可能是这个字符集缺少偏僻汉字,无法解释其内容,所以偏僻字变成了乱码了。
最后的解决办法是,在Windows中文环境下正常编写原程序,用默认的方式编译生成class文件,或者编译时指定参数-encoding GB18030,这样汉字都能正常解释并转换为UTF-8存储在class文件中,在运行的时候,我们需要制定参数,java –Dfile.encoding=GB18030 。。。。。,系统环境使用默认英文即可,这样JVM就不会根据系统的环境设定默认字符集,而是所有输入输出都使用我们指定的字符集,这样不但解决了英文环境下的中文输出问题,而且还解决了偏僻字的显示问题。
最后附上汉字的转码过程:
1.‘中文’的GB18030编码为d6 d0 ce c4&对应java源码文件
经过ISO8859-1解码为UTF-8为81 30 89 30 81 30 88 34 81 30 88 32 81 30 87 32 对应编译过程
经过ISO8859-1编码为d6 d0 ce c4 对应Java程序输出汉字的过程
d6 d0 ce c4经GB18030解释为‘中文’二字 对应系统显示汉字的过程。
在这个过程中,虽然中间出现了乱码,但是信息没有丢,最后还是能还原为中文的,是比较蹩脚的处理过程。
2. ‘中文’二字的UTF-8编码为e4 b8 ad e6 96 87,对应正常编译后的class文件存储内容
经过ISO8859-1编码为3f 3f,已经出错,丢失信息,对应java程序汉字输出过程
3f 3f经GB18030解释为汉字为??,乱码,无法还原, 对应系统显示汉字的过程
这个过程中,信息丢失,是个完全错误的处理过程。
3. ‘中文’的GB18030编码为d6 d0 ce c4&对应java源码文件
经过GB18030解码为UTF-8为e4 b8 ad e6 96 87 对应编译过程
经过GB18030编码为d6 d0 ce c4 对应Java程序输出汉字的过长
d6 d0 ce c4经GB18030解释为‘中文’二字 对应系统显示汉字的过程。
这个过程是最为理想的处理过程,没有丢失信息,也没有出现任何蹩脚的信息。
java乱码的根本原因就是:java可以设置字符编码的地方太多,只要有不统一的地方就有出现乱码。
----------------------------------------------------------------------------------------------------------------------------------
*************************************java、jsp中设置编码******************************************/&
首先说在java里那些地方能够设置编码&
开发工具会有好多地方设置编码这个不解少了,这里不介绍了。&
下面两种设置编码格式方法适用于jsp页面(*.jsp)&
&%@ page language=&java& import=&java.util.*& pageEncoding=&UTF-8&%&&
&%@ page contentType=&text/ charset=UTF-8& %&&
下面方式适合于jsp、servlet、action中(*.java)&
request.setCharacterEncoding(&UTF-8&);&
response.setCharacterEncoding(&UTF-8&);&
下面适合html页面(*.*.html)&
&meta http-equiv=&content-type& content=&text/ charset=UTF-8&&&
Tomcate设置编码(server.xml)&
&Connector 其他省略 port=&80& URIEncoding=&UTF-8&&&
mysql设置编码命令&
SET character_set_client = utf8;&
SET character_set_connection = utf8;&
SET character_set_database = utf8;&
SET character_set_results = utf8;/*这里要注意很有用*/&
SET character_set_server = utf8;&
SET collation_connection = utf8_&
SET collation_database = utf8_&
SET collation_server = utf8_&
my.ini中配置默认编码&
default-character-set=utf8&
连接数据库设置编码&
jdbc:mysql://192.168.0.5:3306/test?characterEncoding=utf8&
/*****************************************java与mysq编码对应****************************************/&
java中的常用编码UTF-8;GBK;GB2312;ISO-8859-1;&
对应mysql数据库中的编码utf8;gb2312;latin1&
/********************************************过滤器使用*********************************************/&
//过滤器设置编码过滤(SetCharacterEncodingFilter.java)&
package com.&
import java.io.*;&
import javax.servlet.*;&
import javax.servlet.http.*;&
public class SetCharacterEncodingFilter extends HttpServlet implements Filter{&
&& private FilterConfig filterC&
&& private String encoding=&
&& //Handle the passed-in FilterConfig&
&& public void init(FilterConfig filterConfig){&
&&&&& this.filterConfig=filterC&
&&&&& encoding=filterConfig.getInitParameter(&encoding&);&
&& //Process the request/response pair&
&& public void doFilter(ServletRequest request,ServletResponse response,FilterChain filterChain){&
&&&&& try{&
&&&&&&&& request.setCharacterEncoding(encoding);&
&&&&&&&& filterChain.doFilter(request,response);&
&&&&& } catch(ServletException sx){&
&&&&&&&& filterConfig.getServletContext().log(sx.getMessage());&
&&&&& } catch(IOException iox){&
&&&&&&&& filterConfig.getServletContext().log(iox.getMessage());&
&& //Clean up resources&
&& public void destroy(){&
//web.xml配置过滤器方法(web.xmd)&
&&& &filter-name&setcharacterencodingfilter&/filter-name&&
&&& &filter-class&com.sorc.SetCharacterEncodingFilter&/filter-class&&
&&& &init-param&&
&&&&& &param-name&encoding&/param-name&&
&&&&& &param-value&utf8&/param-value&&
&&& &/init-param&&
&/filter&&
&filter-mapping&&
&&& &filter-name&setcharacterencodingfilter&/filter-name&&
&&& &url-pattern&/*&/url-pattern&&
&/filter-mapping&&
/************************有了上面的基础下面试完满解决方案*****************************************/&
1.使用GBK编码的解决方案&
这个最简单 遇到设置编码的地方就是用GBK数据库gbk 然后在使用个过滤器过滤编码为gbk一切搞定。&
效果为添加数据无乱码 读出无乱码 数据库管理工具无乱码 到处sql结构和数据无乱码&
2.使用UTF-8编码解决方案&
所有编码都设置为UTF-8&
数据库编码utf8&
设置过滤器编码utf8&
数据库连接?characterEncoding=utf8&
然后在数据库管理工具或mysql命令行 运行 SET character_set_results =&
效果为添加数据无乱码 读出无乱码 数据库管理工具无乱码 到处sql结构和数据时存在乱码&
3.页面使用UTF8 数据库使用latin1的解决方案&
jap java tomcat 设置为UTF-8&
过滤器 utf8&
数据库连接?characterEncoding=latin1&
数据库其他latin1&
然后在数据库管理工具或mysql命令行 运行 SET character_set_results =&
效果为添加数据无乱码 读出无乱码 数据库管理工具无乱码 到处sql结构和数据时存在乱码&
以上都不需要页面或java代码中手动转码
----------------------------------------------------------------------------------------------------------------------------------
一、JSP页面显示乱码二、表单提交中文时出现乱码三、数据库连接
大家在JSP的开发过程中,经常出现中文乱码的问题,可能一至困扰着您,我现在把我在JSP开发中遇到的中文乱码的问题及解决办法写出来供大家参考。
一、JSP页面显示乱码
下面的显示页面(display.jsp)就出现乱码:
&title&JSP的中文处理&/title&
&meta http-equiv=&Content-Type& content=&text/ charset=gb2312&&
out.print(&JSP的中文处理&);
对不同的WEB服务器和不同的JDK版本,处理结果就不一样。原因:服务器使用的编码方式不同和浏览器对不同的字符显示结果不同而导致的。解决办法:在 JSP页面中指定编码方式(gb2312),即在页面的第一行加上:&%@ page contentType=&text/ charset=gb2312&%&,就可以消除乱码了。完整页面如下:
&%@ page contentType=&text/ charset=gb2312&%&
&title&JSP的中文处理&/title&
&meta http-equiv=&Content-Type& content=&text/ charset=gb2312&&
out.print(&JSP的中文处理&);
二、表单提交中文时出现乱码
下面是一个提交页面(submit.jsp),代码如下:
&title&JSP的中文处理&/title&
&meta http-equiv=&Content-Type& content=&text/ charset=gb2312&&
&form name=&form1& method=&post& action=&process.jsp&&
&div align=&center&&
&input type=&text& name=&name&&
&input type=&submit& name=&Submit& value=&Submit&&
下面是处理页面(process.jsp)代码:
&%@ page contentType=&text/ charset=gb2312&%&
&title&JSP的中文处理&/title&
&meta http-equiv=&Content-Type& content=&text/ charset=gb2312&&
&%=request.getParameter(&name&)%&
如果submit.jsp提交英文字符能正确显示,如果提交中文时就会出现乱码。原因:浏览器默认使用UTF-8编码方式来发送请求,而UTF- 8和 GB2312编码方式表示字符时不一样,这样就出现了不能识别字符。解决办法:通过request.seCharacterEncoding (&gb2312&)对请求进行统一编码,就实现了中文的正常显示。修改后的process.jsp代码如下:
&%@ page contentType=&text/ charset=gb2312&%&
request.seCharacterEncoding(&gb2312&);
&title&JSP的中文处理&/title&
&meta http-equiv=&Content-Type& content=&text/ charset=gb2312&&
&%=request.getParameter(&name&)%&
三、数据库连接出现乱码
只要涉及中文的地方全部是乱码,解决办法:在数据库的数据库URL中加上
useUnicode=true&characterEncoding=GBK&就OK了。
四、数据库的显示乱码
在mysql4.1.0中,varchar类型,text类型就会出现中文乱码,对于varchar类型把它设为binary属性就可以解决中文问题,对于text类型就要用一个编码转换类来处理,实现如下:
public class Convert {
/** 把ISO-8859-1码转换成GB2312
public static String ISOtoGB(String iso){
if(iso.equals(&&) || iso == null){
return &&;
iso = iso.trim();
gb = new String(iso.getBytes(&ISO-8859-1&),&GB2312&);
catch(Exception e){
System.err.print(&编码转换错误:&+e.getMessage());
return &&;
把它编译成class,就可以调用Convert类的静态方法ISOtoGB()来转换编码。
如果你还有什么不懂之处:我给大家推荐一个好的JSP-JAVA网站:
http://www.phy./dsp/
1.&& 在jsp中&%@ page contentType=&text/ charset=A& %&如果指定了,那么在改jsp中所有构造的String(不是引用),如果沒有指定编码,那么这些String的编码是A的。
&&&& 从request的得到的String如果沒有指定request的编码的话,他是iso-8859-1的
&&&& 从别的地方得到的String是使用原來初始的编码的,比如从数据库得到String,如果数据库的编码是B,那么该String的编码是B而不是A的,也不是系统默认的。
&&&& 此时,如果要输出的String的编码不是A,那么,很可能显示乱码的,所以首先要将String正確转化为编码A的String,然后输出。
2.&& 在jsp中&%@ page contentType=&text/ charset=A& %&沒有指定,那么相当于指定了&%@ page contentType=&text/ charset=ISO-8859-1& %&
3. Servelte中如果执行了像 response.setContentType(&text/charset=A&);説明将response的字符输出流编码设置为A,所有要输出的String的编码要转化为A的,否則会得到乱码的。
&&&& Servelet中从request得到的String的编码和jsp中一样的,但是在servlet java文件中构造的String是使用的系统默认的编
--------------------------------------------------------------------------
根本解决办法 所有地方都设置成GBK
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:254175次
积分:3005
积分:3005
排名:第4490名
原创:39篇
转载:124篇
评论:24条
(1)(3)(4)(1)(3)(4)(14)(3)(5)(7)(71)(47)详细探讨字节码和字符码已经如果解决乱码问题和中文显示问题
查看 (800)
积分2523 等级6 入室弟子
引言“字符与编码”是一个被经常讨论的话题。即使这样,时常出现的乱码仍然困扰着大家。虽然我们有很多的办法可以用来消除乱码,但我们并不一定理解这些办法的内在原理。而有的乱码产生的原因,实际上由于底层代码本身有问题所导致的。因此,不仅是初学者会对字符编码感到模糊,有的底层开发人员同样对字符编码缺乏准确的理解。1. 编码问题的由来,相关概念的理解1.1 字符与编码的发展从计算机对多国语言的支持角度看,大致可以分为三个阶段: 系统内码说明系统阶段一ASCII计算机刚开始只支持英语,其它语言不能够在计算机上存储和显示。英文 DOS阶段二ANSI编码(本地化)为使计算机支持更多语言,通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。比如:汉字 '中' 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。中文 DOS,中文 Windows 95/98,日文 Windows 95/98阶段三UNICODE(国际化)为了使国际间信息交流更加方便,国际组织制定了 UNICODE 字符集,为各种语言中的每一个字符设定了统一并且唯一的数字编号,以满足跨语言、跨平台进行文本转换、处理的要求。Windows NT/2000/XP,Linux,Java字符串在内存中的存放方法:在 ASCII 阶段,单字节字符串使用一个字节存放一个字符(SBCS)。比如,&Bob123& 在内存中为:426F6231323300Bob123\0在使用 ANSI 编码支持多种语言阶段,每个字符使用一个字节或多个字节来表示(MBCS),因此,这种方式存放的字符也被称作多字节字符。比如,&中文123& 在中文 Windows 95 内存中为7个字节,每个汉字占2个字节,每个英文和数字字符占1个字节:D6D0CEC431323300中文123\0在 UNICODE 被采用之后,计算机存放字符串时,改为存放每个字符在 UNICODE 字符集中的序号。目前计算机一般使用 2 个字节(16 位)来存放一个序号(DBCS),因此,这种方式存放的字符也被称作宽字节字符。比如,字符串 &中文123& 在 Windows 2000 下,内存中实际存放的是 5 个序号:2D4E87653100320033000000&&&& ← 在 x86 CPU 中,低字节在前中文123\0 一共占 10 个字节。1.2 字符,字节,字符串理解编码的关键,是要把字符的概念和字节的概念理解准确。这两个概念容易混淆,我们在此做一下区分: 概念描述举例字符人们使用的记号,抽象意义上的一个符号。'1', '中', 'a', '$', '¥', ……字节计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间。0x01, 0x45, 0xFA, ……ANSI字符串在内存中,如果“字符”是以 ANSI 编码形式存在的,一个字符可能使用一个字节或多个字节来表示,那么我们称这种字符串为 ANSI 字符串或者多字节字符串。&中文123&(占7字节)UNICODE字符串在内存中,如果“字符”是以在 UNICODE 中的序号存在的,那么我们称这种字符串为 UNICODE 字符串或者宽字节字符串。L&中文123&(占10字节)由于不同 ANSI 编码所规定的标准是不相同的,因此,对于一个给定的多字节字符串,我们必须知道它采用的是哪一种编码规则,才能够知道它包含了哪些“字符”。而对于 UNICODE 字符串来说,不管在什么环境下,它所代表的“字符”内容总是不变的。1.3 字符集与编码各个国家和地区所制定的不同 ANSI 编码标准中,都只规定了各自语言所需的“字符”。比如:汉字标准(GB2312)中没有规定韩国语字符怎样存储。这些 ANSI 编码标准所规定的内容包含两层含义:使用哪些字符。也就是说哪些汉字,字母和符号会被收入标准中。所包含“字符”的集合就叫做“字符集”。 规定每个“字符”分别用一个字节还是多个字节存储,用哪些字节来存储,这个规定就叫做“编码”。 各个国家和地区在制定编码标准的时候,“字符的集合”和“编码”一般都是同时制定的。因此,平常我们所说的“字符集”,比如:GB2312, GBK, JIS 等,除了有“字符的集合”这层含义外,同时也包含了“编码”的含义。“UNICODE 字符集”包含了各种语言中使用到的所有“字符”。用来给 UNICODE 字符集编码的标准有很多种,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等。1.4 常用的编码简介简单介绍一下常用的编码规则,为后边的章节做一个准备。在这里,我们根据编码规则的特点,把所有的编码分成三类:分类编码标准说明单字节字符编码ISO-8859-1最简单的编码规则,每一个字节直接作为一个 UNICODE 字符。比如,[0xD6, 0xD0] 这两个字节,通过 iso-8859-1 转化为字符串时,将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符,即 &&OÐ&。反之,将 UNICODE 字符串通过 iso-8859-1 转化为字节串时,只能正常转化 0~255 范围的字符。ANSI 编码GB2312,BIG5,Shift_JIS,ISO-8859-2 ……把 UNICODE 字符串通过 ANSI 编码转化为“字节串”时,根据各自编码的规定,一个 UNICODE 字符可能转化成一个字节或多个字节。反之,将字节串转化成字符串时,也可能多个字节转化成一个字符。比如,[0xD6, 0xD0] 这两个字节,通过 GB2312 转化为字符串时,将得到 [0x4E2D] 一个字符,即 '中' 字。“ANSI 编码”的特点:1. 这些“ANSI 编码标准”都只能处理各自语言范围之内的 UNICODE 字符。2. “UNICODE 字符”与“转换出来的字节”之间的关系是人为规定的。UNICODE 编码UTF-8,UTF-16, UnicodeBig ……与“ANSI 编码”类似的,把字符串通过 UNICODE 编码转化成“字节串”时,一个 UNICODE 字符可能转化成一个字节或多个字节。与“ANSI 编码”不同的是:1. 这些“UNICODE 编码”能够处理所有的 UNICODE 字符。2. “UNICODE 字符”与“转换出来的字节”之间是可以通过计算得到的。我们实际上没有必要去深究每一种编码具体把某一个字符编码成了哪几个字节,我们只需要知道“编码”的概念就是把“字符”转化成“字节”就可以了。对于“UNICODE 编码”,由于它们是可以通过计算得到的,因此,在特殊的场合,我们可以去了解某一种“UNICODE 编码”是怎样的规则。2. 字符与编码在程序中的实现2.1 程序中的字符与字节在 C++ 和 Java 中,用来代表“字符”和“字节”的数据类型,以及进行编码的方法:类型或操作C++Java字符wchar_tchar字节charbyteANSI 字符串char[]byte[]UNICODE 字符串wchar_t[]String字节串→字符串mbstowcs(), MultiByteToWideChar()string = new String(bytes, &encoding&)字符串→字节串wcstombs(), WideCharToMultiByte()bytes = string.getBytes(&encoding&)以上需要注意几点:Java 中的 char 代表一个“UNICODE 字符(宽字节字符)”,而 C++ 中的 char 代表一个字节。 MultiByteToWideChar() 和 WideCharToMultiByte() 是 Windows API 函数。 2.2 C++ 中相关实现方法声明一段字符串常量:// ANSI 字符串,内容长度 7 字节char&&&& sz[20] = &中文123&;// UNICODE 字符串,内容长度 5 个 wchar_t(10 字节)wchar_t wsz[20] = L&\x4E2D\x\x&;UNICODE 字符串的 I/O 操作,字符与字节的转换操作:// 运行时设定当前 ANSI 编码,VC 格式setlocale(LC_ALL, &.936&);// GCC 中格式setlocale(LC_ALL, &zh_CN.GBK&);// Visual C++ 中使用小写 %s,按照 setlocale 指定编码输出到文件// GCC 中使用大写 %Sfwprintf(fp, L&%s\n&, wsz);// 把 UNICODE 字符串按照 setlocale 指定的编码转换成字节wcstombs(sz, wsz, 20);// 把字节串按照 setlocale 指定的编码转换成 UNICODE 字符串mbstowcs(wsz, sz, 20);在 Visual C++ 中,UNICODE 字符串常量有更简单的表示方法。如果源程序的编码与当前默认 ANSI 编码不符,则需要使用 #pragma setlocale,告诉编译器源程序使用的编码:// 如果源程序的编码与当前默认 ANSI 编码不一致,// 则需要此行,编译时用来指明当前源程序使用的编码#pragma setlocale(&.936&)// UNICODE 字符串常量,内容长度 10 字节wchar_t wsz[20] = L&中文123&;以上需要注意 #pragma setlocale 与 setlocale(LC_ALL, &&) 的作用是不同的,#pragma setlocale 在编译时起作用,setlocale() 在运行时起作用。2.3 Java 中相关实现方法字符串类 String 中的内容是 UNICODE 字符串:// Java 代码,直接写中文String string = &中文123&;// 得到长度为 5,因为是 5 个字符System.out.println(string.length());字符串 I/O 操作,字符与字节转换操作。在 Java 包 java.io.* 中,以“Stream”结尾的类一般是用来操作“字节串”的类,以“Reader”,“Writer”结尾的类一般是用来操作“字符串”的类。// 字符串与字节串间相互转化// 按照 GB2312 得到字节(得到多字节字符串)byte [] bytes = string.getBytes(&GB2312&);// 从字节按照 GB2312 得到 UNICODE 字符串string = new String(bytes, &GB2312&);// 要将 String 按照某种编码写入文本文件,有两种方法:// 第一种办法:用 Stream 类写入已经按照指定编码转化好的字节串OutputStream os = new FileOutputStream(&1.txt&);os.write(bytes);os.close();// 第二种办法:构造指定编码的 Writer 来写入字符串Writer ow = new OutputStreamWriter(new FileOutputStream(&2.txt&), &GB2312&);ow.write(string);ow.close();/* 最后得到的 1.txt 和 2.txt 都是 7 个字节 */如果 java 的源程序编码与当前默认 ANSI 编码不符,则在编译的时候,需要指明一下源程序的编码。比如:E:\&javac -encoding BIG5 Hello.java以上需要注意区分源程序的编码与 I/O 操作的编码,前者是在编译时起作用,后者是在运行时起作用。3. 几种误解,以及乱码产生的原因和解决办法3.1 容易产生的误解 对编码的误解误解一在将“字节串”转化成“UNICODE 字符串”时,比如在读取文本文件时,或者通过网络传输文本时,容易将“字节串”简单地作为单字节字符串,采用每“一个字节”就是“一个字符”的方法进行转化。而实际上,在非英文的环境中,应该将“字节串”作为 ANSI 字符串,采用适当的编码来得到 UNICODE 字符串,有可能“多个字节”才能得到“一个字符”。通常,一直在英文环境下做开发的程序员们,容易有这种误解。误解二在 DOS,Windows 98 等非 UNICODE 环境下,字符串都是以 ANSI 编码的字节形式存在的。这种以字节形式存在的字符串,必须知道是哪种编码才能被正确地使用。这使我们形成了一个惯性思维:“字符串的编码”。当 UNICODE 被支持后,Java 中的 String 是以字符的“序号”来存储的,不是以“某种编码的字节”来存储的,因此已经不存在“字符串的编码”这个概念了。只有在“字符串”与“字节串”转化时,或者,将一个“字节串”当成一个 ANSI 字符串时,才有编码的概念。不少的人都有这个误解。第一种误解,往往是导致乱码产生的原因。第二种误解,往往导致本来容易纠正的乱码问题变得更复杂。在这里,我们可以看到,其中所讲的“误解一”,即采用每“一个字节”就是“一个字符”的转化方法,实际上也就等同于采用 iso-8859-1 进行转化。因此,我们常常使用 bytes = string.getBytes(&iso-8859-1&) 来进行逆向操作,得到原始的“字节串”。然后再使用正确的 ANSI 编码,比如 string = new String(bytes, &GB2312&),来得到正确的“UNICODE 字符串”。3.2 非 UNICODE 程序在不同语言环境间移植时的乱码非 UNICODE 程序中的字符串,都是以某种 ANSI 编码形式存在的。如果程序运行时的语言环境与开发时的语言环境不同,将会导致 ANSI 字符串的显示失败。比如,在日文环境下开发的非 UNICODE 的日文程序界面,拿到中文环境下运行时,界面上将显示乱码。如果这个日文程序界面改为采用 UNICODE 来记录字符串,那么当在中文环境下运行时,界面上将可以显示正常的日文。由于客观原因,有时候我们必须在中文操作系统下运行非 UNICODE 的日文软件,这时我们可以采用一些工具,比如,南极星,AppLocale 等,暂时的模拟不同的语言环境。3.3 网页提交字符串当页面中的表单提交字符串时,首先把字符串按照当前页面的编码,转化成字节串。然后再将每个字节转化成 &%XX& 的格式提交到 Web 服务器。比如,一个编码为 GB2312 的页面,提交 &中& 这个字符串时,提交给服务器的内容为 &%D6%D0&。在服务器端,Web 服务器把收到的 &%D6%D0& 转化成 [0xD6, 0xD0] 两个字节,然后再根据 GB2312 编码规则得到 &中& 字。在 Tomcat 服务器中,request.getParameter() 得到乱码时,常常是因为前面提到的“误解一”造成的。默认情况下,当提交 &%D6%D0& 给 Tomcat 服务器时,request.getParameter() 将返回 [0x00D6, 0x00D0] 两个 UNICODE 字符,而不是返回一个 &中& 字符。因此,我们需要使用 bytes = string.getBytes(&iso-8859-1&) 得到原始的字节串,再用 string = new String(bytes, &GB2312&) 重新得到正确的字符串 &中&。3.4 从数据库读取字符串通过数据库客户端(比如 ODBC 或 JDBC)从数据库服务器中读取字符串时,客户端需要从服务器获知所使用的 ANSI 编码。当数据库服务器发送字节流给客户端时,客户端负责将字节流按照正确的编码转化成 UNICODE 字符串。如果从数据库读取字符串时得到乱码,而数据库中存放的数据又是正确的,那么往往还是因为前面提到的“误解一”造成的。解决的办法还是通过 string = new String( string.getBytes(&iso-8859-1&), &GB2312&) 的方法,重新得到原始的字节串,再重新使用正确的编码转化成字符串。3.5 电子邮件中的字符串当一段 Text 或者 HTML 通过电子邮件传送时,发送的内容首先通过一种指定的字符编码转化成“字节串”,然后再把“字节串”通过一种指定的传输编码(Content-Transfer-Encoding)进行转化得到另一串“字节串”。比如,打开一封电子邮件源代码,可以看到类似的内容:Content-Type: text/&&&&&&& charset=&gb2312&Content-Transfer-Encoding: base64sbG+qcrQuqO17cf4yee74bGjz9W7+b3wudzA7dbQ0MQNCg0KvPKzxqO6uqO17cnnsaPW0NDEDQoNCg==最常用的 Content-Transfer-Encoding 有 Base64 和 Quoted-Printable 两种。在对二进制文件或者中文文本进行转化时,Base64 得到的“字节串”比 Quoted-Printable 更短。在对英文文本进行转化时,Quoted-Printable 得到的“字节串”比 Base64 更短。邮件的标题,用了一种更简短的格式来标注“字符编码”和“传输编码”。比如,标题内容为 &中&,则在邮件源代码中表示为:// 正确的标题格式Subject: =?GB2312?B?1tA=?=其中,第一个“=?”与“?”中间的部分指定了字符编码,在这个例子中指定的是 GB2312。 “?”与“?”中间的“B”代表 Base64。如果是“Q”则代表 Quoted-Printable。 最后“?”与“?=”之间的部分,就是经过 GB2312 转化成字节串,再经过 Base64 转化后的标题内容。 如果“传输编码”改为 Quoted-Printable,同样,如果标题内容为 &中&:// 正确的标题格式Subject: =?GB2312?Q?=D6=D0?=如果阅读邮件时出现乱码,一般是因为“字符编码”或“传输编码”指定有误,或者是没有指定。比如,有的发邮件组件在发送邮件时,标题 &中&:// 错误的标题格式Subject: =?ISO-8859-1?Q?=D6=D0?=这样的表示,实际上是明确指明了标题为 [0x00D6, 0x00D0],即 &&OÐ&,而不是 &中&。4. 几种错误理解的纠正误解:“ISO-8859-1 是国际编码?”非也。iso-8859-1 只是单字节字符集中最简单的一种,也就是“字节编号”与“UNICODE 字符编号”一致的那种编码规则。当我们要把一个“字节串”转化成“字符串”,而又不知道它是哪一种 ANSI 编码时,先暂时地把“每一个字节”作为“一个字符”进行转化,不会造成信息丢失。然后再使用 bytes = string.getBytes(&iso-8859-1&) 的方法可恢复到原始的字节串。误解:“Java 中,怎样知道某个字符串的内码?”Java 中,字符串类 java.lang.String 处理的是 UNICODE 字符串,不是 ANSI 字符串。我们只需要把字符串作为“抽象的符号的串”来看待。因此不存在字符串的内码的问题。
楼主最新博客

我要回帖

更多关于 request乱码 的文章

 

随机推荐