TOMCAT中文问题,解决(全)(转载)

TOMCAT中文问题解决了.其他应用服务器的问题也可以得到更好的理解,对于解决中文问题,和一些国际化的问题,会有更多的帮助...本文转载自CSDN (Tomcat中文问题解决一,二,三,四)
-------------------------------------------------------------------------------------------------------------------------------------------------------
Tomcat 的中文处理(一)
看到很多朋友问关于中文的处理问题,下面我们以 tomcat4.0 servlet jsp 引擎来说说 unicode 的处理。
1)       从客户端接受请求
当客户端请求 tomcat 的一个 jsp 文档的时候, tomcat 会构造相应的 httpServletRequest 实现类的实例来代表客户端,通过对流 servletInputStream 读,我们可以得到客户端来的数据。
   jsp 中我们通常使用的 request.getParameter() 来得到参数的值,这个函数的背后到底怎么样的呢?怎么样对 String 编码的呢?
  通过 tomcat httpServletRequest 实现类源代码考察:
public String getParameter(String name)
    {
        parseParameters();/ 处理 parameters
        String values[] = (String[])parameters.get(name);// 得到该参数名字对应的 Object( 是一个数组 )
        if(values != null)
        {
            return values[0];
        } else
        {
            return null;
        }
    }
其中 parameters request 的一个 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 数组进行处理,构造 key value, 就是参数名字和参数值:
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 编码的。
 
而且我们看看 tomcat jsp 产生的 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 (JSP out 对象 ) 写出数据的时候,会调用如下的函数来初始化
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 進制流,但是在同一時刻只能打開使用一個流的。至於兩者的關係,我們在後面介紹。 Jsp out 對象就是 response 的字符流的。
  同樣的 request 也存在一個字符流和一個 2 進制流,但是在同一時刻只能打開使用一個流的。
response 两个流的关系
    我们来考察 response 实现类 getOutputStream() getWriter 函数的实现
public ServletOutputStream getOutputStream()        throws IOException
    {
      。。。。。。。。。。。。。。。。。。。。。
            stream = createOutputStream();/// 创建 response 2 进制 输出流
   。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
        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 是要輸出 String char 數組
 {
          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-1 String 的。
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 你好 //name1 name 转化来 的,是 GB2312 的,所以正常顯示
name is ISO-8859-1????/name ISO-8859-1 的不能正常顯示的
name21 is 直接 ??????????????????? 由於 name2 GB2312 編碼的 , 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 使用的系統 默认的编码方式的。
但是在 servlet request 得到的字符,如果沒有指定 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>"); /// 输出一个编码为本地默认的 String response 中,但是此时 response 的编码是 iso-8859-1 的, 所以出现乱码得
        out.println("title is "+title); title 系统默认的编码 得到乱码
         out.println("<br>name is "+name) ;/name request 的來的, 正确显示
        out.println("</body>");
        out.println("</html>");
        System.out.println(" 你好 ") ; 由于 Console 是系統默認編碼,所以正確顯示
        System.out.println("title is "+title); /title 是默認編碼的,正常顯示
        System.out.println("name is " +name ); ///name iso-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>
完!
关于中文处理的问题就写这些了!
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值