TOMCAT中文問題,解決(全)

TOMCAT中文問題解決了.其他應用服務器的問題也可以得到更好的理解,對於解決中文問題,和一些國際化的問題,會有更多的幫助...本文轉載自CSDN (Tomcat中文問題解決一,二,三,四)

-------------------------------------------------------------------------------------------------------------------------------------------------------

原地址: http://www.csdn.net/develop/article/17/17204.shtm

http://www.csdn.net/develop/article/17/17222.shtm

http://www.csdn.net/develop/article/17/17223.shtm

http://www.csdn.net/develop/article/17/17225.shtm

http://www.csdn.net/develop/article/17/17233.shtm

作者:[email protected]

Tomcat的中文處理(一)

看到很多朋友問關於中文的處理問題,下面我們以tomcat4.0servletjsp引擎來說說unicode的處理。

1)       從客戶端接受請求

當客戶端請求tomcat的一個jsp文檔的時候,tomcat會構造相應的httpServletRequest實現類的實例來代表客戶端,通過對流servletInputStream讀,我們可以得到客戶端來的數據。

   jsp中我們通常使用的request.getParameter()來得到參數的值,這個函數的背後到底怎麼樣的呢?怎麼樣對String編碼的呢?

  通過tomcathttpServletRequest實現類源代碼考察:

public String getParameter(String name)

    {

        parseParameters();/////////處理parameters

        String values[] = (String[])parameters.get(name);//得到該參數名字對應的Object(是一個數組)

        if(values != null)

        {

            return values[0];

        } else

        {

            return null;

        }

    }

其中parametersrequest的一個map類型的數據成員,用來存放接受到的客戶端的數據。也就是說每當客戶端請求的時候,tomcat構造一個request實例,該實例有一個parameters用來存放從servlet實例的寫入流的讀來的客戶端的數據。

  從上面的代碼知道最重要的的是parseParameters()函數,它是來處理parameters的。

下面來看看:

protected void parseParameters()

    {

        if(parsed)

        {

            return;///如果處理過了,就不要處理了

        }

        ParameterMap results = parameters;/////構造parameters對象的本地引用

        if(results == null)

        {

            results = new ParameterMap();//////如果沒有實例

        }

  results.setLocked(false);

        String encoding = getCharacterEncoding();//////////////////////////得到httpServeltRequest的編碼

        if(encoding == null)

        {

            encoding = "ISO-8859-1";//////////如果沒有指定httpServeltRequest的編碼採用"ISO-8859-1"

        }

       。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

            RequestUtil.parseParameters(results, queryString, encoding);//////////////////////處理編碼

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

                             

                is.read(buf, len, max - len); //////////////////////從流中讀取數據

           。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

                RequestUtil.parseParameters(results, buf, encoding);///////////////////////////////////處理編碼

         。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

        parameters = results;//////重置引用

    }

下面再來看看RequestUtil.parseParameters(results, buf, encoding);/的處理:

在此就不貼源代碼了,

RequestUtil.parseParameters(results, buf, encoding)的處理中對於buf  byte數組進行處理,構造keyvalue,就是參數名字和參數值:

while(ix < data.length)

            {

                byte c = data[ix++];

                switch((char)c)

                {

                case 38: // '&'

                    value = new String(data, 0, ox, encoding);

                    if(key != null)

                    {

                        putMapEntry(map, key, value);

                        key = null;

                    }

                    ox = 0;

                    break;

 

                case 61: // '='

    key = new String(data, 0, ox, encoding);

                    ox = 0;

                    break;

 

                case 43: // '+'

                    data[ox++] = 32;

                    break;

 

                case 37: // '%'

                    data[ox++] = (byte)((convertHexDigit(data[ix++]) << 4) + convertHexDigit(data[ix++]));

                    break;

 

                default:

                    data[ox++] = c;

                    break;

                }

            }

            if(key != null)

            {

                value = new String(data, 0, ox, encoding);

                putMapEntry(map, key, value);

            }

 

 

顯然對於參數名字和參數的值都是採用的new String(data, 0, ox, encoding);方法來使用指定的編碼方式構造的。

結論:我們不難看出如果沒有指定request的編碼方式,那麼從客戶端接受到的參數的名字和參數值都是以iso-8859-1編碼的String的。

   也就是說我們在jsp的頁面中的表單元素中給出的參數值在通過request.getParamter()得到後的String是以iso-8859-1編碼的。

 

而且我們看看tomcatjsp產生的java文件知道,對於在jsp定義的沒有指定編碼方式的String的時候,tomcat是使用的iso-8859-1方式的,而不是系統默認的。

  比如:

<%

String name=new String(“你好”);或者String name=”你好”;/////都是使用的iso-8859-1的編碼方式的。

System.out.println(name);/////////////////就會產生亂碼的。(因爲Console使用的系統的默認編碼的,中文系統是gb2321,日文是MS932).

%>

下篇我們介紹httpServletResponse的處理

 

 

 

 

 

Tomcat的中文處理(二)

 

上篇我們介紹了tomcat是怎麼對接收到字符進行編碼的,現在我們來看當向客戶端寫html文檔的時候到底發生了什麼?

 

tomcate在向客戶端寫出數據的時候,使用的是response的輸出流來實現的。但是jsp是怎樣使用response的流的呢?

在使用JSP內含對象out輸出的時候,out是一個JspWriter實現類的對象實例,JspWriterImpl(ServletResponse response, int sz, boolean autoFlush)是一個該類的構造函數,其使用到了response,在JspWriterImpl內部還有一個java.io.Writer對象實例的引用,在使用JspWriter(JSPout對象)寫出數據的時候,會調用如下的函數來初始化

protected void initOut() throws IOException

    {

        if(out == null)

        {

            out = response.getWriter();/////////初始化 java.io.Writer對象

        }

    }來初始化該內部對象的。

然後在jspWriter的各個輸出數據的函數的實現中就是調用上面的java.io.Writer對象的方法的。

    所以不論jsp或者是servlet,對客戶端寫出html的時候,都是通過response.getWriter();得到的字符流或者由getOutputStream()得到2進制流的。

   一個response存在一個字符流,也存在一個2進制流,但是在同一時刻只能打開使用一個流的。至於兩者的關係,我們在後面介紹。Jspout對象就是response的字符流的。

  同樣的request也存在一個字符流和一個2進制流,但是在同一時刻只能打開使用一個流的。

response兩個流的關係

    我們來考察response的實現類的getOutputStream()getWriter函數的實現:

public ServletOutputStream getOutputStream()        throws IOException

    {

      。。。。。。。。。。。。。。。。。。。。。

            stream = createOutputStream();///創建response2進制的輸出流

   。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

        return stream;

    }

public PrintWriter getWriter()        throws IOException

 {

     。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

           ResponseStream newStream = (ResponseStream)createOutputStream();////////創建2進制流

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

            OutputStreamWriter osr = new OutputStreamWriter(newStream, getCharacterEncoding());

            writer = new ResponseWriter(osr, newStream);///得到response的字符輸出流

 。。。。。。。。。。。。。。。。。。。。。。。。。。

        }

    }

顯然,我們的字符流就是從2進制轉化而來的

    還有兩個函數要注意:

public String getCharacterEncoding()//////response的編碼,默認是ISO-8859-1的

    {

        if(encoding == null)//////////////////////////////////如果沒有指定編碼

        {

            return "ISO-8859-1";

        } else

        {

            return encoding;

        }

    }

public void setContentType(String type);設置response的類型和編碼

    {

      。。。。。。。。。。。。。

            encoding = RequestUtil.parseCharacterEncoding(type);////////得到指定的編碼

            if(encoding == null)

            {

                encoding = "ISO-8859-1";//////////////////////////如果沒有指定編碼方式

            }

        } else

        if(encoding != null)

        {

            contentType = type + ";charset=" + encoding;

        }

    }

好了,現在我們知道了在寫出字符的時候使用的response的字符流(不管是jsp或者servlet),也就是使用的OutputStreamWriter osr = new OutputStreamWriter(newStream, getCharacterEncoding());

注意的是newStream是response的2進制流的實現。

所以我們還得看看OutputStreamWriter的實現:

考察OutputStreamWriter的源代碼,他有一個StreamEncoder 類型的對象,就是依靠他來轉換編碼的;

StreamEncoder是由sun公司提供的,它有一個

public static StreamEncoder forOutputStreamWriter(OutputStream outputstream, Object obj, String s)來得到StreamEncoder對象實例。

對於jsp,servlet來說在構造他的時候 outputstream參數是response的2進制流,obj是OutputStreamWriter對象,s就是編碼方式的名字。其實得到是一個StreamEncoder的子類的對象實例,

     return new CharsetSE(outputstream, obj, Charset.forName(s1)); CharsetSE是StreamEncoder的子類。

他有一個如下的函數來實現編碼轉換的:

void implWrite(char ac[], int i, int j)throws IOException /////// ac是要輸出Stringchar數組

 {

          CharBuffer charbuffer = CharBuffer.wrap(ac, i, j);

          。。。。。。。。。。。。。。。。。。。。。。。

          CoderResult coderresult = encoder.encode(charbuffer, bb, false);/////bb是ByteBuffer,存放編碼後的byte緩衝區

      。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

writeBytes();///////////////////////////////將bb轉化到byte數組寫入到response的2進制流中

      。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

 }

 

至此,我們瞭解了tomcat背後的編碼轉換過程

Tomcat的中文處理(三):

前面廢話講過了,現在我們來分析幾個例子:

 

1)jsp中如果使用了:

<%@ page contentType="text/html; charset=Shift_JIS" %>

他其實就是指定了response的類型和字符的編碼方式,上面指定了response的字符編碼是是Shift_JIS。

在jsp中構造String的時候,如果沒有明確指定String的編碼,String使用的編碼就是charset指定的;如果charset沒有指定字符的編碼的話,那麼話,就使用ISO-8859-1

注意的是如果沒有指定requset的編碼,那麼從request得到的String都是iso-8859-1編碼的。(上一篇已經講過了。),他和charset是沒有關係的。

如果要輸出的String的編碼和response的編碼不一樣的話,就很可能出現亂碼的情況。

舉個例子:

<%@ page contentType="text/html; charset=GB2312" %>///////////////指定response編碼爲中文簡體,那所有的要輸出的字符都要使用和GB2312相適應的編碼

<html>

<head><title></title>

</head>

<body>

<%

String name=request.getParameter("name");////////得到客戶端的參數,沒有指定request編碼,所以它是編碼爲iso-8859-1String的。

String name1=new String(name.getBytes("ISO-8859-1"),"GB2312");//////////轉化爲中文簡體的編碼

String name2="你好";/////直接定義String,使用reponse編碼這裏是GB2312的。

String name21=new String(name2.getBytes("ISO-8859-1")," GB2312");////////從name2轉化

System.out.println("name1 is GB2312"+name1);

System.out.println("name is ISO-8859-1"+name);

System.out.println("name21 is 直接"+name21);

System.out.println("我們大家");

%>

<form action="./B.jsp" method="POST">

<input type="text" name="name" value="<%=name1%>">

<input type="submit">

</form>

<hr>

name1 is GB2312  <%=name1%><br>

 

name is ISO-8859-1     <%=name%><br>

 

name21 is 直接<%=name21%><br>

 

<%="我們大家"%></body>

</html>

結果:

console中:(他對應response的編碼是GB2312的,日文系統是MS932)

name1 is GB2312  你好//////////name1name轉化來的,是GB2312的,所以正常顯示

name is ISO-8859-1????/////////////nameISO-8859-1的不能正常顯示的

name21 is 直接???????????????????////////////////由於name2GB2312編碼的,name21 =new String(name2.getBytes("GB2312"),"MS932"))發生了錯誤的轉化,所以不能正常的現實,如果將ISO-8859-1換為GB2312就可以了。

我們大家//////////////////////////////////jsp中定義的string是採用<%@ page contentType="text/html; charset= GB2312" %>指定的編碼,如果沒有指定,就使用iso-8859-1的。

   可以看到我們在ie中看到的結果是一樣的。

下面我們將<%@ page contentType="text/html; charset=Shift_JIS" %>去掉。

結果:

console (這個時候,Console的編碼是GB2312,所以編碼爲GB2312的字符能顯示,由於在jsp中構造的String此時使用的iso-8859-1,所以不能顯示)

name1 is GB2312你好

name is ISO-8859-1????

name21 is ???? 你好/////////////////name2的編碼此時爲iso-8859-1,所以轉化來的name21是正確的

????????

ie  (這個時候response編碼iso-8859-1,所以編碼爲iso-8859-1的能顯示由於在jsp構造String此時使用的iso-8859-1,所以也能顯示)

name1 is GB2312??

name is ISO-8859-1 你好

name21 is 直接 ???????????????????

我們大家

 

顯然不一樣了結果!!!!

Tomcat的中文處理(四)

 

 

2)在servlet和其他java文件中的字符

這種情況下,構造String使用的系統默認的編碼方式的。

但是在servletrequest得到的字符,如果沒有指定request的編碼,那麼就是得到的一個編碼方式爲iso-8859-1的字符,在servlet中,如果沒有指定response編碼方式(通過setContentType),那麼response使用的iso-8859-1編碼方式

 

例子

import javax.servlet.*;

import javax.servlet.http.*;

 

 

public class HelloWorldExample extends HttpServlet {

 

 

    public void doGet(HttpServletRequest request,

                      HttpServletResponse response)

        throws IOException, ServletException

    {

        String name=request.getParameter("name");/////得到name參數的value

        response.setContentType("text/html ");///不設置編碼,此時response使用iso-8859-1的編碼

        PrintWriter out = response.getWriter();//得到字符流,此時的編碼為iso-8859-1

 

        out.println("<html>");

        out.println("<head>");

 

String title="你好";/////構造一個String,注意的是雖然此時沒有爲response指定編碼,但是在servlet構造String使用的系統默認的編碼的。

        out.println("<title>sdsfdsfsdfds</title>");

        out.println("</head>");

        out.println("<body bgcolor=/"white/">");

        out.println("我們大家<br>");///////輸出一個編碼爲本地默認的Stringresponse中,但是此時response的編碼是iso-8859-1的,所以出現亂碼得

        out.println("title is "+title);////title系統默認的編碼得到亂碼

         out.println("<br>name is "+name);/////namerequest的來的,正確顯示

        out.println("</body>");

        out.println("</html>");

        System.out.println("你好");////由於Console是系統默認編碼,所以正確顯示

        System.out.println("title is "+title);/////title是默認編碼的,正常顯示

        System.out.println("name is "+name);///////nameiso-8859-1的編碼的,亂碼

    }

}

如果我們在response.setContentType("text/html ");改為:response.setContentType("text/html charset=GB2312");那麼,IE的輸出和Console輸出是一樣的。

 

總結:

1.                      jsp<%@ page contentType="text/html; charset=A" %>如果指定了,那麼jsp中所有構造的String(不是引用),如果沒有指定編碼那麼這些String編碼A的。

request的得到的String如果沒有指定request編碼的話,他是iso-8859-1

從別的地方得到的String是使用原來初始的編碼的,比如從數據庫得到String,如果數據庫的編碼B,那麼該String編碼B而不是A,也不是系統默認的

      此時,如果要輸出String編碼不是A,那麼,很可能顯示亂碼的,所以首先要String正確轉化爲編碼AString,然後輸出

2.                      jsp<%@ page contentType="text/html; charset=A" %>沒有指定,那麼相當於指定了<%@ page contentType="text/html; charset=ISO-8859-1" %>

3.      Servelte中如果執行了像 response.setContentType("text/html;charset=A");説明response字符輸出流編碼設置爲A,所有要輸出的String編碼要轉化爲A的,否則會得到亂碼的

       Serveletrequest得到的String編碼jsp一樣的但是在servlet java文件中構造的String是使用的系統默認的編碼的。servelt從外部得到的String 是使用原來的編碼的,比如從編碼爲B數據庫得到的數據編碼爲B,不是A,也不是系統默認的編碼

 

 

後語: 雖然我們使用的tomcat來作說明,其他的jsp,servlet引擎其實現的方法也差不多!

          使用filter來改變request的編碼

在前面的文章裏面,我們討論了在tomcat下的jsp和servlet的字符編碼問題!

知道當沒有指定request的編碼的時候,從客戶端得到的數據是iso-8859-1編碼的(request.getParameter()得到傳遞的參數值);

但是我們怎麼來改變request的編碼呢?

方法有很多種!

 比如:在getRequestDispatcher("/jsp/jsptoserv/hello.jsp").forward(request, response);之前修改

request的編碼,那麼在jsp/jsptoserv/hello.jsp中得到的參數值就是制定的編碼的字符。

本文我們使用Filter來修改request的編碼!

 

1)首先編寫filter類:

package myFilter;


import java.io.IOException;
import javax.servlet.*;

public class ChangeCharsetFilter implements Filter {


    protected String encoding = null;/////要制定的編碼,在web.xml中配置

    protected FilterConfig filterConfig = null;

        public void destroy() {

        this.encoding = null;
        this.filterConfig = null;

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
 throws IOException, ServletException {

            if (request.getCharacterEncoding() == null){
            String encoding = getEncoding();////得到指定的編碼名字
            if (encoding != null)
                request.setCharacterEncoding(encoding);////設置request的編碼
        }

         chain.doFilter(request, response);///有機會執行下一個filter

    }

    public void init(FilterConfig filterConfig) throws ServletException {

          this.filterConfig = filterConfig;
        this.encoding = filterConfig.getInitParameter("encoding");///得到在web.xml中配置的編碼
  
    }


    protected String getEncoding() {

        return (this.encoding);///得到指定的編碼

    }


}


2。編輯web.xml文件

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
 PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

<filter>
        <filter-name>SetCharacterEncoding</filter-name>
        <filter-class>myFilter.ChangeCharsetFilter </filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>GB2312</param-value>//////指定編碼爲GB2312
        </init-param>
     </filter>
    <filter-mapping>
        <filter-name>SetCharacterEncoding</filter-name>
        <url-pattern>/*</url-pattern>////////對於所有的request改變其編碼
    </filter-mapping>

</web-app>
///

3。

寫一個a.jsp

<%@ page contentType="text/html; charset=GB2312" %>
<html>
<head></head>
<body>
<%
String name=request.getParameter("name");///本來這裏得到字符是iso-8859-1編碼的,不能直接

在Console中輸出的,但是現在改變了request的編碼方式,此時的name的編碼是GB2312,所以能正確在Console中顯示的。

System.out.println(name);

%>
<form action="a.jsp" method="post">
<input type="text" name="name">
<input type="submit">
</form>
<%=name%>
</body>
</html>

完!

關於中文處理的問題就寫這些了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章