Java中文乱码处理

java编码转换过程

咱们老是用一个java类文件和用户进行最直接的交互(输入、输出),这些交互内容包含的文字可能会包含中文。不管这些java类是与数据库交互,仍是与前端页面交互,他们的生命周期老是这样的:javascript

一、程序员在操做系统上经过编辑器编写程序代码而且以.java的格式保存操做系统中,这些文件咱们称之为源文件。html

二、经过JDK中的javac.exe编译这些源文件造成.class类。前端

三、直接运行这些类或者部署在WEB容器中运行,获得输出结果。java

这些过程是从宏观上面来观察的,了解这个确定是不行的,咱们须要真正来了解java是如何来编码和被解码的:mysql

第一步:当咱们用编辑器编写java源文件,程序文件在保存时会采用操做系统默认的编码格式(通常咱们中文的操做系统采用的是GBK编码格式)造成一个.java文件。java源文件是采用操做系统默认支持的file.encoding编码格式保存的。下面代码能够查看系统的file.encoding参数值。程序员

System.out.println(System.getProperty("file.encoding"));  

第二步:当咱们使用javac.exe编译咱们的java文件时,JDK首先会确认它的编译参数encoding来肯定源代码字符集,若是咱们不指定该编译参数,JDK首先会获取操做系统默认的file.encoding参数,而后JDK就会把咱们编写的java源程序从file.encoding编码格式转化为JAVA内部默认的UNICODE格式放入内存中。web

第三步:JDK将上面编译好的且保存在内存中信息写入class文件中,造成.class文件。此时.class文件是Unicode编码的,也就是说咱们常见的.class文件中的内容不管是中文字符仍是英文字符,他们都已经转换为Unicode编码格式了。sql

在这一步中对对JSP源文件的处理方式有点儿不一样:WEB容器调用JSP编译器,JSP编译器首先会查看JSP文件是否设置了文件编码格式,若是没有设置则JSP编译器会调用调用JDK采用默认的编码方式将JSP文件转化为临时的servlet类,而后再编译为.class文件并保持到临时文件夹中。chrome

第四步:运行编译的类:在这里会存在一下几种状况数据库

一、直接在console上运行。

二、JSP/Servlet类。

三、java类与数据库之间。

这三种状况每种状况的方式都会不一样,

1.Console上运行的类

这种状况下,JVM首先会把保存在操做系统中的class文件读入到内存中,这个时候内存中class文件编码格式为Unicode,而后JVM运行它。若是须要用户输入信息,则会采用file.encoding编码格式对用户输入的信息进行编码同时转换为Unicode编码格式保存到内存中。程序运行后,将产生的结果再转化为file.encoding格式返回给操做系统并输出到界面去。整个流程以下:

201412260001

在上面整个流程中,凡是涉及的编码转换都不能出现错误,不然将会产生乱码。

2.Servlet类

因为JSP文件最终也会转换为servlet文件(只不过存储的位置不一样而已),因此这里咱们也将JSP文件归入其中。

当用户请求Servlet时,WEB容器会调用它的JVM来运行Servlet。首先JVM会把servlet的class加载到内存中去,内存中的servlet代码是Unicode编码格式的。而后JVM在内存中运行该Servlet,在运行过程当中若是须要接受从客户端传递过来的数据(如表单和URL传递的数据),则WEB容器会接受传入的数据,在接收过程当中若是程序设定了传入参数的的编码则采用设定的编码格式,若是没有设置则采用默认的ISO-8859-1编码格式,接收的数据后JVM会将这些数据进行编码格式转换为Unicode而且存入到内存中。运行Servlet后产生输出结果,同时这些输出结果的编码格式仍然为Unicode。紧接着WEB容器会将产生的Unicode编码格式的字符串直接发送置客户端,若是程序指定了输出时的编码格式,则按照指定的编码格式输出到浏览器,不然采用默认的ISO-8859-1编码格式。整个过程流程图以下:

2014122700001

3.数据库部分

咱们知道java程序与数据库的链接都是经过JDBC驱动程序来链接的,而JDBC驱动程序默认的是ISO-8859-1编码格式的,也就是说咱们经过java程序向数据库传递数据时,JDBC首先会将Unicode编码格式的数据转换为ISO-8859-1的编码格式,而后在存储在数据库中,即在数据库保存数据时,默认格式为ISO-8859-1。

201412270001

编码&解码

在上篇博客中LZ阐述了三个渠道的编码转换过程,下面LZ将结束java在那些场合须要进行编码和解码操做,并详序中间的过程,进一步掌握java的编码和解码过程。在java中主要有四个场景须要进行编码解码操做:

1:I/O操做

2:内存

3:数据库

4:javaWeb

下面主要介绍前面两种场景,数据库部分只要设置正确编码格式就不会有什么问题,javaWeb场景过多须要了解URL、get、POST的编码,servlet的解码,因此javaWeb场景下节LZ介绍。

I/O操做

在前面LZ就提过乱码问题无非就是转码过程当中编码格式的不统一产生的,好比编码时采用UTF-8,解码采用GBK,但最根本的缘由是字符到字节或者字节到字符的转换出问题了,而这中状况的转换最主要的场景就是I/O操做的时候。固然I/O操做主要包括网络I/O(也就是javaWeb)和磁盘I/O。网络I/O下节介绍。

首先咱们先看I/O的编码操做。

201412300001

InputStream为字节输入流的全部类的超类,Reader为读取字符流的抽象类。java读取文件的方式分为按字节流读取和按字符流读取,其中InputStream、Reader是这两种读取方式的超类。

按字节

咱们通常都是使用InputStream.read()方法在数据流中读取字节(read()每次都只读取一个字节,效率很是慢,咱们通常都是使用read(byte[])),而后保存在一个byte[]数组中,最后转换为String。在咱们读取文件时,读取字节的编码取决于文件所使用的编码格式,而在转换为String过程当中也会涉及到编码的问题,若是二者之间的编码格式不一样可能会出现问题。例如存在一个问题test.txt编码格式为UTF-8,那么经过字节流读取文件时所得到的数据流编码格式就是UTF-8,而咱们在转化成String过程当中若是不指定编码格式,则默认使用系统编码格式(GBK)来解码操做,因为二者编码格式不一致,那么在构造String过程确定会产生乱码,以下:

File file = new File("C:\\test.txt");  
InputStream input = new FileInputStream(file);  
StringBuffer buffer = new StringBuffer();  
byte[] bytes = new byte[1024];  
for(int n ; (n = input.read(bytes))!=-1 ; ){  
    buffer.append(new String(bytes,0,n));  
}  
System.out.println(buffer); 

输出结果:锘挎垜鏄?cm

test.txt中的内容为:我是 cm。

要想不出现乱码,在构造String过程当中指定编码格式,使得编码解码时二者编码格式保持一致便可:

buffer.append(new String(bytes,0,n,"UTF-8")); 

按字符

其实字符流能够看作是一种包装流,它的底层仍是采用字节流来读取字节,而后它使用指定的编码方式将读取字节解码为字符。在java中Reader是读取字符流的超类。因此从底层上来看按字节读取文件和按字符读取没什么区别。在读取的时候字符读取每次是读取留个字节,字节流每次读取一个字节。

字节&字符转换

字节转换为字符必定少不了InputStreamReader。API解释以下:InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集能够由名称指定或显式给定,或者能够接受平台默认的字符集。 每次调用 InputStreamReader 中的一个 read() 方法都会致使从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,能够提早从底层流读取更多的字节,使其超过知足当前读取操做所需的字节。API解释很是清楚,InputStreamReader在底层读取文件时仍然采用字节读取,读取字节后它须要根据一个指定的编码格式来解析为字符,若是没有指定编码格式则采用系统默认编码格式。

String file = "C:\\test.txt";   
         String charset = "UTF-8";   
         // 写字符换转成字节流  
         FileOutputStream outputStream = new FileOutputStream(file);   
         OutputStreamWriter writer = new OutputStreamWriter(outputStream, charset);   
         try {   
            writer.write("我是 cm");   
         } finally {   
            writer.close();   
         }   
           
         // 读取字节转换成字符  
         FileInputStream inputStream = new FileInputStream(file);   
         InputStreamReader reader = new InputStreamReader(   
         inputStream, charset);   
         StringBuffer buffer = new StringBuffer();   
         char[] buf = new char[64];   
         int count = 0;   
         try {   
            while ((count = reader.read(buf)) != -1) {   
                buffer.append(buf, 0, count);   
            }   
         } finally {   
            reader.close();   
         }  
         System.out.println(buffer);  

内存

首先咱们看下面这段简单的代码

String s = "我是 cm";   
byte[] bytes = s.getBytes();   
String s1 = new String(bytes,"GBK");   
String s2 = new String(bytes);

在这段代码中咱们看到了三处编码转换过程(一次编码,两次解码)。先看String.getTytes():

public byte[] getBytes() {  
        return StringCoding.encode(value, 0, value.length);  
    }  

内部调用StringCoding.encode()方法操做:

static byte[] encode(char[] ca, int off, int len) {  
        String csn = Charset.defaultCharset().name();  
        try {  
            // use charset name encode() variant which provides caching.  
            return encode(csn, ca, off, len);  
        } catch (UnsupportedEncodingException x) {  
            warnUnsupportedCharset(csn);  
        }  
        try {  
            return encode("ISO-8859-1", ca, off, len);  
        } catch (UnsupportedEncodingException x) {  
            // If this code is hit during VM initialization, MessageUtils is  
            // the only way we will be able to get any kind of error message.  
            MessageUtils.err("ISO-8859-1 charset not available: "  
                             + x.toString());  
            // If we can not find ISO-8859-1 (a required encoding) then things  
            // are seriously wrong with the installation.  
            System.exit(1);  
            return null;  
        }  
    }  

encode(char[] paramArrayOfChar, int paramInt1, int paramInt2)方法首先调用系统的默认编码格式,若是没有指定编码格式则默认使用ISO-8859-1编码格式进行编码操做,进一步深刻以下:

String csn = (charsetName == null) ? "ISO-8859-1" : charsetName; 

一样的方法能够看到new String 的构造函数内部是调用StringCoding.decode()方法:

public String(byte bytes[], int offset, int length, Charset charset) {  
        if (charset == null)  
            throw new NullPointerException("charset");  
        checkBounds(bytes, offset, length);  
        this.value =  StringCoding.decode(charset, bytes, offset, length);  
    }  

decode方法和encode对编码格式的处理是同样的。

对于以上两种状况咱们只须要设置统一的编码格式通常都不会产生乱码问题。

编码&编码格式

首先先看看java编码类图[1]

201412300003

首先根据指定的chart设置ChartSet类,而后根据ChartSet建立ChartSetEncoder对象,最后再调用 CharsetEncoder.encode 对字符串进行编码,不一样的编码类型都会对应到一个类中,实际的编码过程是在这些类中完成的。下面时序图展现详细的编码过程:

201412300002

经过这编码的类图和时序图能够了解编码的详细过程。下面将经过一段简单的代码对ISO-8859-一、GBK、UTF-8编码

public class Test02 {  
    public static void main(String[] args) throws UnsupportedEncodingException {  
        String string = "我是 cm";  
        Test02.printChart(string.toCharArray());  
        Test02.printChart(string.getBytes("ISO-8859-1"));  
        Test02.printChart(string.getBytes("GBK"));  
        Test02.printChart(string.getBytes("UTF-8"));  
    }  
      
    /** 
     * char转换为16进制 
     */  
    public static void printChart(char[] chars){  
        for(int i = 0 ; i < chars.length ; i++){  
            System.out.print(Integer.toHexString(chars[i]) + " ");   
        }  
        System.out.println("");  
    }  
      
    /** 
     * byte转换为16进制 
     */  
    public static void printChart(byte[] bytes){  
        for(int i = 0 ; i < bytes.length ; i++){  
            String hex = Integer.toHexString(bytes[i] & 0xFF);   
             if (hex.length() == 1) {   
               hex = '0' + hex;   
             }   
             System.out.print(hex.toUpperCase() + " ");   
        }  
        System.out.println("");  
    }  
}   
-------------------------outPut:  
6211 662f 20 63 6d   
3F 3F 20 63 6D   
CE D2 CA C7 20 63 6D   
E6 88 91 E6 98 AF 20 63 6D 

 

经过程序咱们能够看到“我是 cm”的结果为:

char[]:6211 662f 20 63 6d

ISO-8859-1:3F 3F 20 63 6D 
GBK:CE D2 CA C7 20 63 6D 
UTF-8:E6 88 91 E6 98 AF 20 63 6D

图以下:

201412310001

编码&解码

经过下图咱们能够了解在javaWeb中有哪些地方有转码:

201501060001

用户想服务器发送一个HTTP请求,须要编码的地方有url、cookie、parameter,通过编码后服务器接受HTTP请求,解析HTTP请求,而后对url、cookie、parameter进行解码。在服务器进行业务逻辑处理过程当中可能须要读取数据库、本地文件或者网络中的其余文件等等,这些过程都须要进行编码解码。当处理完成后,服务器将数据进行编码后发送给客户端,浏览器通过解码后显示给用户。在这个整个过程当中涉及的编码解码的地方较多,其中最容易出现乱码的位置就在于服务器与客户端进行交互的过程。

上面整个过程能够归纳成这样,页面编码数据传递给服务器,服务器对得到的数据进行解码操做,通过一番业务逻辑处理后将最终结果编码处理后传递给客户端,客户端解码展现给用户。因此下面我就请求对javaweb的编码&解码进行阐述。

请求

客户端想服务器发送请求无非就经过四中状况:

一、URL方式直接访问。

二、页面连接。

三、表单get提交

四、表单post提交

URL方式

对于URL,若是该URL中所有都是英文的那却是没有什么问题,若是有中文就要涉及到编码了。如何编码?根据什么规则来编码?又如何来解码呢?下面LZ将一一解答!首先看URL的组成部分:

201501060002

在这URL中浏览器将会对path和parameter进行编码操做。为了更好地解释编码过程,使用以下URL

http://127.0.0.1:8080/perbank/我是cm?name=我是cm

将以上地址输入到浏览器URL输入框中,经过查看http 报文头信息咱们能够看到浏览器是如何进行编码的。下面是IE、Firefox、Chrome三个浏览器的编码状况:

201501080001

201501080002

201501080003

能够看到各大浏览器对“我是”的编码状况以下:

 

path部分

Query String

Firefox

E6 88 91 E6 98 AF

E6 88 91 E6 98 AF

Chrome

E6 88 91 E6 98 AF

E6 88 91 E6 98 AF

IE

E6 88 91 E6 98 AF

CE D2 CA C7

查阅上篇博客的编码可知对于path部分Firefox、chrome、IE都是采用UTF-8编码格式,对于Query String部分Firefox、chrome采用UTF-8,IE采用GBK。至于为何会加上%,这是由于URL的编码规范规定浏览器将ASCII字符非 ASCII 字符按照某种编码格式编码成 16 进制数字而后将每一个 16 进制表示的字节前加上“%”。

固然对于不一样的浏览器,相同浏览器不一样版本,不一样的操做系统等环境都会致使编码结果不一样,上表某一种状况,对于URL编码规则下任何结论都是过早的。因为各大浏览器、各个操做系统对URL的URI、QueryString编码均可能存在不一样,这样对服务器的解码势必会形成很大的困扰,下面咱们将已tomcat,看tomcat是如何对URL进行解码操做的。

解析请求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,这个方法把传过来的 URL 的 byte[] 设置到 org.apache.coyote.Request 的相应的属性中。这里的 URL 仍然是 byte 格式,转成 char 是在 org.apache.catalina.connector.CoyoteAdapter 的 convertURI 方法中完成的:

protected void convertURI(MessageBytes uri, Request request)   
             throws Exception {   
                    ByteChunk bc = uri.getByteChunk();   
                    int length = bc.getLength();   
                    CharChunk cc = uri.getCharChunk();   
                    cc.allocate(length, -1);   
                    String enc = connector.getURIEncoding();     //获取URI解码集  
                    if (enc != null) {   
                        B2CConverter conv = request.getURIConverter();   
                        try {   
                            if (conv == null) {   
                                conv = new B2CConverter(enc);   
                                request.setURIConverter(conv);   
                            }   
                        } catch (IOException e) {...}   
                        if (conv != null) {   
                            try {   
                                conv.convert(bc, cc, cc.getBuffer().length - cc.getEnd());   
                                uri.setChars(cc.getBuffer(), cc.getStart(), cc.getLength());   
                                return;   
                            } catch (IOException e) {...}   
                        }   
                    }   
                    // Default encoding: fast conversion   
                    byte[] bbuf = bc.getBuffer();   
                    char[] cbuf = cc.getBuffer();   
                    int start = bc.getStart();   
                    for (int i = 0; i < length; i++) {   
                        cbuf[i] = (char) (bbuf[i + start] & 0xff);   
                    }   
                    uri.setChars(cbuf, 0, length);   
    }  

从上面的代码可知,对URI的解码操做是首先获取Connector的解码集,该配置在server.xml中

<Connector URIEncoding="utf-8"  />   

若是没有定义则会采用默认编码ISO-8859-1来解析。

对于Query String部分,咱们知道不管咱们是经过get方式仍是POST方式提交,全部的参数都是保存在Parameters,而后咱们经过request.getParameter,解码工做就是在第一次调用getParameter方法时进行的。在getParameter方法内部它调用org.apache.catalina.connector.Request 的 parseParameters 方法,这个方法将会对传递的参数进行解码。下面代码只是parseParameters方法的一部分:

//获取编码  
   String enc = getCharacterEncoding();  
  //获取ContentType 中定义的 Charset  
  boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();  
  if (enc != null) {    //若是设置编码不为空,则设置编码为enc  
      parameters.setEncoding(enc);  
      if (useBodyEncodingForURI) {   //若是设置了Chartset,则设置queryString的解码为ChartSet  
          parameters.setQueryStringEncoding(enc);      
      }  
  } else {     //设置默认解码方式  
      parameters.setEncoding(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);  
      if (useBodyEncodingForURI) {  
          parameters.setQueryStringEncoding(org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);  
      }  
  }  

从上面代码能够看出对query String的解码格式要么采用设置的ChartSet要么采用默认的解码格式ISO-8859-1。注意这个设置的ChartSet是在 http Header中定义的ContentType,同时若是咱们须要改指定属性生效,还须要进行以下配置:

<Connector URIEncoding="UTF-8" useBodyEncodingForURI="true"/>  

上面部分详细介绍了URL方式请求的编码解码过程。其实对于咱们而言,咱们更多的方式是经过表单的形式来提交。

表单GET

咱们知道经过URL方式提交数据是很容易产生乱码问题的,因此咱们更加倾向于经过表单形式。当用户点击submit提交表单时,浏览器会更加设定的编码来编码数据传递给服务器。经过GET方式提交的数据都是拼接在URL后面(能够当作query String??)来提交的,因此tomcat服务器在进行解码过程当中URIEncoding就起到做用了。tomcat服务器会根据设置的URIEncoding来进行解码,若是没有设置则会使用默认的ISO-8859-1来解码。假如咱们在页面将编码设置为UTF-8,而URIEncoding设置的不是或者没有设置,那么服务器进行解码时就会产生乱码。这个时候咱们通常能够经过new String(request.getParameter("name").getBytes("iso-8859-1"),"utf-8") 的形式来获取正确数据。

表单POST

对于POST方式,它采用的编码也是由页面来决定的即contentType。当我经过点击页面的submit按钮来提交表单时,浏览器首先会根据ontentType的charset编码格式来对POST表单的参数进行编码而后提交给服务器,在服务器端一样也是用contentType中设置的字符集来进行解码(这里与get方式就不一样了),这就是经过POST表单提交的参数通常而言都不会出现乱码问题。固然这个字符集编码咱们是能够本身设定的:request.setCharacterEncoding(charset) 。

咱们知道JSP页面是须要转换为servlet的,在转换过程当中确定是要进行编码的。在JSP转换为servlet过程当中下面一段代码起到相当重要的做用。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="GBK" %>  

在上面代码中有两个地方存在编码:pageEncoding、contentType的charset。其中pageEncoding是jsp文件自己的编码,而contentType的charset是指服务器发送给客户端时的内容编码。

jsp在转换为Servlet的过程当中是须要通过主要的三次编码转换过程(除去数据库编码转换、页面参数输入编码转换):

第一次:转换为.java文件;

第二次:转换为.class文件;

第三次:业务逻辑处理后输出。

第一阶段

JVM将JSP编译为.jsp文件。在这个过程当中pageEncoding就起到做用了,JVM首先会获取pageEncoding的值,若是该值存在则采用它设定的编码来编译,不然则采用file.encoding编码来编译。

第二阶段

JVM将.java文件转换为.class文件。在这个过程就与任何编码的设置都没有关系了,无论JSP采用了什么样的编码格式都将无效。通过这个阶段后.jsp文件就转换成了统一的Unicode格式的.class文件了。

第三阶段

后台通过业务逻辑处理后将产生的结果输出到客户端。在这个过程当中contentType的charset就发挥了功效。若是设置了charset则浏览器就会使用指定的编码格式进行解码,不然采用默认的ISO-8859-1编码格式进行解码处理。

流程如以下:

201501150001

 


 

咱们主要经过两种形式提交向服务器发送请求:URL、表单。而表单形式通常都不会出现乱码问题,乱码问题主要是在URL上面。经过前面几篇博客的介绍咱们知道URL向服务器发送请求编码过程实在是实在太混乱了。不一样的操做系统、不一样的浏览器、不一样的网页字符集,将致使彻底不一样的编码结果。若是程序员要把每一种结果都考虑进去,是否是太恐怖了?有没有办法,可以保证客户端只用一种编码方法向服务器发出请求?

有!这里我主要提供如下几种方法

1、javascript

使用javascript编码不给浏览器插手的机会,编码以后再向服务器发送请求,而后在服务器中解码。在掌握该方法的时候,咱们须要料及javascript编码的三个方法:escape()、encodeURI()、encodeURIComponent()。

escape

采用SIO Latin字符集对指定的字符串进行编码。全部非ASCII字符都会被编码为%xx格式的字符串,其中xx表示该字符在字符集中所对应的16进制数字。例如,格式对应的编码为%20。它对应的解码方法为unescape()。

201501150002

事实上escape()不能直接用于URL编码,它的真正做用是返回一个字符的Unicode编码值。好比上面“我是cm”的结果为%u6211%u662Fcm,其中“我”对应的编码为6211,“是”的编码为662F,“cm”编码为cm。

注意,escape()不对"+"编码。可是咱们知道,网页在提交表单的时候,若是有空格,则会被转化为+字符。服务器处理数据的时候,会把+号处理成空格。因此,使用的时候要当心。

encodeURI

对整个URL进行编码,它采用的是UTF-8格式输出编码后的字符串。不过encodeURI除了ASCII编码外对于一些特殊的字符也不会进行编码如:! @ # $& * ( ) = : / ; ? + '。

201501150003

encodeURIComponent

把URI字符串采用UTF-8编码格式转化成escape格式的字符串。相对于encodeURI,encodeURIComponent会更增强大,它会对那些在encodeURI()中不被编码的符号(; / ? : @ & = + $ , #)通通会被编码。可是encodeURIComponent只会对URL的组成部分进行个别编码,而不用于对整个URL进行编码。对应解码函数方法decodeURIComponent。

固然咱们通常都是使用encodeURI方来进行编码操做。所谓的javascript两次编码后台两次解码就是使用该方法。javascript解决该问题有一次转码、两次转码两种解决方法。

一次转码

javascript转码: 

var url = '<s:property value="webPath" />/ShowMoblieQRCode.servlet?name=我是cm';  
window.location.href = encodeURI(url);  

 

转码后的URL:http://127.0.0.1:8080/perbank/ShowMoblieQRCode.servlet?name=%E6%88%91%E6%98%AFcm

后台处理:

String name = request.getParameter("name");  
System.out.println("前台传入参数:" + name);  
name  = new String(name.getBytes("ISO-8859-1"),"UTF-8");  
System.out.println("通过解码后参数:" + name);  

输出结果:

前台传入参数:??????cm 
通过解码后参数:我是cm

二次转码

var url = '<s:property value="webPath" />/ShowMoblieQRCode.servlet?name=我是cm';  
window.location.href = encodeURI(encodeURI(url));   

转码后的url:http://127.0.0.1:8080/perbank/ShowMoblieQRCode.servlet?name=%25E6%2588%2591%25E6%2598%25AFcm

后台处理:

String name = request.getParameter("name");  
System.out.println("前台传入参数:" + name);  
name  = URLDecoder.decode(name,"UTF-8");  
System.out.println("通过解码后参数:" + name);  

输出结果:

前台传入参数:E68891E698AFcm 

通过解码后参数:我是cm

filter

使用过滤器,过滤器LZ提供两种,第一种设置编码,第二种直接在过滤器中进行解码操做。

过滤器1

该过滤器是直接设置request的编码格式的。

public class CharacterEncoding implements Filter {  
  
    private FilterConfig config ;  
    String encoding = null;  
      
    public void destroy() {  
        config = null;  
    }  
  
    public void doFilter(ServletRequest request, ServletResponse response,  
            FilterChain chain) throws IOException, ServletException {  
        request.setCharacterEncoding(encoding);  
        chain.doFilter(request, response);  
    }  
  
    public void init(FilterConfig config) throws ServletException {  
        this.config = config;  
        //获取配置参数  
        String str = config.getInitParameter("encoding");  
        if(str!=null){  
            encoding = str;  
        }  
    }  
}  

配置:

<!-- 中文过滤器的配置 -->  
    <filter>  
        <filter-name>chineseEncoding</filter-name>  
        <filter-class>com.test.filter.CharacterEncoding</filter-class>  
          
        <init-param>  
            <param-name>encoding</param-name>  
            <param-value>utf-8</param-value>  
        </init-param>  
    </filter>  
      
    <filter-mapping>  
        <filter-name>chineseEncoding</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping>

过滤器2

该过滤器在处理方法中将参数直接进行解码操做,而后将解码后的参数从新设置到request的attribute中。

public class CharacterEncoding implements Filter {  
    protected FilterConfig filterConfig ;  
    String encoding = null;  
      
    public void destroy() {  
        this.filterConfig = null;  
    }  
  
    /** 
     * 初始化 
     */  
    public void init(FilterConfig filterConfig) {  
        this.filterConfig = filterConfig;  
    }  
  
    /** 
     * 将 inStr 转为 UTF-8 的编码形式 
     *  
     * @param inStr 输入字符串 
     * @return UTF - 8 的编码形式的字符串 
     * @throws UnsupportedEncodingException 
     */  
    private String toUTF(String inStr) throws UnsupportedEncodingException {  
        String outStr = "";  
        if (inStr != null) {  
            outStr = new String(inStr.getBytes("iso-8859-1"), "UTF-8");  
        }  
        return outStr;  
    }  
  
    /** 
     * 中文乱码过滤处理 
     */  
    public void doFilter(ServletRequest servletRequest,  
            ServletResponse servletResponse, FilterChain chain) throws IOException,  
            ServletException {  
        HttpServletRequest request = (HttpServletRequest) servletRequest;  
        HttpServletResponse response = (HttpServletResponse) servletResponse;  
  
        // 得到请求的方式 (1.post or 2.get), 根据不一样请求方式进行不一样处理  
        String method = request.getMethod();  
        // 1. 以 post 方式提交的请求 , 直接设置编码为 UTF-8  
        if (method.equalsIgnoreCase("post")) {  
            try {  
                request.setCharacterEncoding("UTF-8");  
            } catch (UnsupportedEncodingException e) {  
                e.printStackTrace();  
            }  
        }  
        // 2. 以 get 方式提交的请求  
        else {  
            // 取出客户提交的参数集  
            Enumeration<String> paramNames = request.getParameterNames();  
            // 遍历参数集取出每一个参数的名称及值  
            while (paramNames.hasMoreElements()) {  
                String name = paramNames.nextElement(); // 取出参数名称  
                String values[] = request.getParameterValues(name); // 根据参数名称取出其值  
                // 若是参数值集不为空  
                if (values != null) {  
                    // 遍历参数值集  
                    for (int i = 0; i < values.length; i++) {  
                        try {  
                            // 回圈依次将每一个值调用 toUTF(values[i]) 方法转换参数值的字元编码  
                            String vlustr = toUTF(values[i]);  
                            values[i] = vlustr;  
                        } catch (UnsupportedEncodingException e) {  
                            e.printStackTrace();  
                        }  
                    }  
                    // 将该值以属性的形式藏在 request  
                    request.setAttribute(name, values);  
                }  
            }  
  
        }  
        // 设置响应方式和支持中文的字元集  
        response.setContentType("text/html;charset=UTF-8");  
  
        // 继续执行下一个 filter, 无一下个 filter 则执行请求  
        chain.doFilter(request, response);  
    }  
}  

配置:

<!-- 中文过滤器的配置 -->  
    <filter>  
        <filter-name>chineseEncoding</filter-name>  
        <filter-class>com.test.filter.CharacterEncoding</filter-class>  
    </filter>  
      
    <filter-mapping>  
        <filter-name>chineseEncoding</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping> 

其余

一、设置pageEncoding、contentType

<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>  

二、设置tomcat的URIEncoding

在默认状况下,tomcat服务器使用的是ISO-8859-1编码格式来编码的,URIEncoding参数对get请求的URL进行编码,因此咱们只须要在tomcat的server.xml文件的<Connector>标签中加上URIEncoding="utf-8"便可。


-----原文出自:http://cmsblogs.com/?p=1526,请尊重做者辛勤劳动成果,转载说明出处.

-----我的站点:http://cmsblogs.com

相关文章
相关标签/搜索