Servlet 输出中文乱码的新收获

又碰到servlet 输出中文乱码的问题,恼火。研究了一下,有了新的发现和认识。
原始代码:
java 代码
  1. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {   
  2.     PrintWriter pw = response.getWriter();   
  3.     response.setCharacterEncoding("utf-8");   
  4.     response.setContentType("text/html; charset=utf-8");   
  5.     pw.print("中文");   
  6. }  
 无论把3、4两句改成gbk还是utf-8,页面访问到的一律是??
一怒之下用wpe抓包,发现无论设为utf-8还是gbk抓到的均为
HTTP 代码
  1. HTTP/1.1 200 OK   
  2. Server: Apache-Coyote/1.1  
  3. Content-Type: text/html;charset=ISO-8859-1  
  4. Content-Length: 2  
  5. Date: Thu, 08 Mar 2007 06:04:55 GMT   
  6.   
  7. ??  
说明3、4两句没起作用,检查代码,尝试把2和三四顺序调整,乱码问题解决。
检查api文档,发现说明如下
 PrintWriter getWriter()  throws IOException
Returns a PrintWriter object that can send character text to the client. The PrintWriter uses the character encoding returned by getCharacterEncoding(). If the response's character encoding has not been specified as described in getCharacterEncoding (i.e., the method just returns the default value ISO-8859-1), getWriter updates it to ISO-8859-1.   
推断getWriter()返回的PrintWriter使用的charactor encoding是在这个函数返回时即已确定的,但到底是返回的PrintWriter内部属性还是运行时的控制,未找到依据。
查看 tomcat中setCharacterEncoding方法的实现时发现如下代码:

java 代码
  1. public void setCharacterEncoding(String charset) {   
  2.   
  3.         if (isCommitted())   
  4.             return;   
  5.            
  6.         // Ignore any call from an included servlet   
  7.         if (included)   
  8.             return;        
  9.            
  10.         // Ignore any call made after the getWriter has been invoked   
  11.         // The default should be used   
  12.         if (usingWriter)   
  13.             return;   
  14.   
  15.         coyoteResponse.setCharacterEncoding(charset);   
  16.         isCharacterEncodingSet = true;   
  17.     }  
 其中usingWriter 标志为getPrinteWriter方法中设定,可见其控制逻辑为一旦返回了PrintWriter,本函数即不再生效。但是上述的推断没有进一步的证据。
同时我们发现只有usingWriter标志,却没有usingOutputStream标记。猜测使用ServletOutputStream 输出不受此限制,经测试写出如下代码。
java 代码
  1. ServletOutputStream out = response.getOutputStream();   
  2. out.print("中文");   
  3.            
  4. //情况1:正常,浏览器按utf-8方式查看   
  5. //response.setContentType("text/html; charset=utf-8");   
  6.            
  7. //情况2:浏览器缺省按简体中文查看,手动设为utf-8方式查看正常   
  8. //response.setCharacterEncoding("utf-8");  
 说明:这种方式不仅不需要在调用getOutputStream()之前设定字符集,甚至在print输出后设定都有效。
(居然有字数限制,并且提示都没有,内容就丢了,郁闷。)

查看setCharacterEncoding API文档,进一步发现:
Calling setContentType(java.lang.String) with the String of text/html and calling this method with the String of UTF-8 is equivalent with calling setContentType with the String of text/html; charset=UTF-8.
原来只需要用 response.setContentType("text/html; charset=utf-8"); 设定就ok,不需要两次调用。进一步
This method can be called repeatedly to change the character encoding. ......If the character encoding has already been set by setContentType(java.lang.String) or setLocale(java.util.Locale), this method overrides it.
可反复设置,相互覆盖,据此写出如下测试代码
java 代码
 
  1. //情况1:正常,浏览器按utf-8方式查看   
  2. response.setContentType("text/html; charset=gbk");   
  3. response.setCharacterEncoding("utf-8");   
  4. //情况2:正常,浏览器按简体中文方式查看   
  5. //response.setContentType("text/html; charset=utf-8");   
  6. //response.setCharacterEncoding("gbk");   
  7.   
  8. PrintWriter pw = response.getWriter();   
  9. pw.print("中文");  
结论:
1.在servlet中输出中文,如果采用PrintWriter方式,需要在调用getPrintWriter()之前调用 setContentType 或者 setCharacterEncoding;采用ServletOutputStream方式,不受此限。
2. setContentType 和 setCharacterEncoding两方法中设定characterEncoding的方法对服务器效果一致,不需要反复调用。在输出文本内容时,采用 response.setContentType("text/html; charset=utf-8");似乎更为方便。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值