Java/JSP中文乱码问题解决心得
自从接触Java和JSP以来,就不断与Java的中文乱码问题打交道,现在终于得到了彻底的解决,现将我们的解决心得与大家共享。
一、Java中文问题的由来
Java的内核和class文件是基于unicode的,这使Java程序具有良好的跨平台性,但也带来了一些中文乱码问题的麻烦。原因主要有两方面,Java和JSP文件本身编译时产生的乱码问题和Java程序于其他媒介交互产生的乱码问题。
首先Java(包括JSP)源文件中很可能包含有中文,而Java和JSP源文件的保存方式是基于字节流的,如果Java和JSP编译成class文件过程中,使用的编码方式与源文件的编码不一致,就会出现乱码。基于这种乱码,建议在Java文件中尽量不要写中文(注释部分不参与编译,写中文没关系),如果必须写的话,尽量手动带参数-ecoding GBK或-ecoding gb2312编译;对于JSP,在文件头加上<%@ page contentType="text/html;charset=GBK"%>或<%@ page contentType="text/html;charset=gb2312"%>基本上就能解决这类乱码问题。
本文要重点讨论的是第二类乱码,即Java程序与其他存储媒介交互时产生的乱码。很多存储媒介,如数据库,文件,流等的存储方式都是基于字节流的,Java程序与这些媒介交互时就会发生字符(char)与字节(byte)之间的转换,具体情况如下:
从页面form提交数据到java程序 byte->char
从java程序到页面显示 char—>byte
从数据库到java程序 byte—>char
从java程序到数据库 char—>byte
从文件到java程序 byte->char
从java程序到文件 char->byte
从流到java程序 byte->char
从java程序到流 char->byte
如果在以上转换过程中使用的编码方式与字节原有的编码不一致,很可能就会出现乱码。
二、解决方法
前面已经提到了Java程序与其他媒介交互时字符和字节的转换过程,如果这些转换过程中容易产生乱码。解决这些乱码问题的关键在于确保转换时使用的编码方式与字节原有的编码方式保持一致,下面分别论述(Java或JSP自身产生的乱码请参看第一部分)。
1、JSP与页面参数之间的乱码
JSP获取页面参数时一般采用系统默认的编码方式,如果页面参数的编码类型和系统默认的编码类型不一致,很可能就会出现乱码。解决这类乱码问题的基本方法是在页面获取参数之前,强制指定request获取参数的编码方式:request.setCharacterEncoding("GBK")或request.setCharacterEncoding("gb2312")。
如果在JSP将变量输出到页面时出现了乱码,可以通过设置response.setContentType("text/html;charset=GBK")或response.setContentType("text/html;charset=gb2312")解决。
如果不想在每个文件里都写这样两句话,更简洁的办法是使用Servlet规范中的过虑器指定编码,过滤器的在web.xml中的典型配置和主要代码如下:
web.xml:
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>net.vschool.web.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
CharacterEncodingFilter.java:
public class CharacterEncodingFilter implements Filter
{
protected String encoding = null;
public void init(FilterConfig filterConfig) throws ServletException
{
this.encoding = filterConfig.getInitParameter("encoding");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
request.setCharacterEncoding(encoding);
response.setContentType("text/html;charset="+encoding);
chain.doFilter(request, response);
}
}
2、Java与数据库之间的乱码
大部分数据库都支持以unicode编码方式,所以解决Java与数据库之间的乱码问题比较明智的方式是直接使用unicode编码与数据库交互。很多数据库驱动自动支持unicode,如Microsoft的SQLServer驱动。其他大部分数据库驱动,可以在驱动的url参数中指定,如如mm的mysql驱动:jdbc:mysql://localhost/WEBCLDB?useUnicode=true&characterEncoding=GBK。
3、Java与文件/流之间的乱码
Java读写文件最常用的类是FileInputStream/FileOutputStream和FileReader/FileWriter。其中FileInputStream和FileOutputStream是基于字节流的,常用于读写二进制文件。读写字符文件建议使用基于字符的FileReader和FileWriter,省去了字节与字符之间的转换。但这两个类的构造函数默认使用系统的编码方式,如果文件内容与系统编码方式不一致,可能会出现乱码。在这种情况下,建议使用FileReader和FileWriter的父类:InputStreamReader/OutputStreamWriter,它们也是基于字符的,但在构造函数中可以指定编码类型:InputStreamReader(InputStream in, Charset cs) 和OutputStreamWriter(OutputStream out, Charset cs)。
4、其他
上面提到的方法应该能解决大部分乱码问题,如果在其他地方还出现乱码,可能需要手动修改代码。解决Java乱码问题的关键在于在字节与字符的转换过程中,你必须知道原来字节或转换后的字节的编码方式,转换时采用的编码必须与这个编码方式保持一致。我们以前使用Resin服务器,使用smartUpload组件上传文件,上传文件同时传递的中文参数获取没有乱码问题。当在Linux中把Resin设置成服务后,上传文件同时的中文参数获取出现了乱码。这个问题困扰了我们很久,后来我们分析smartUpload组件的源文件,因为文件上传采用的是字节流的方式,里面包含的参数名称和值也是字节流的方式传递的。smartUpload组件读取字节流后再将参数名称和值从字节流中解析出来,问题就出现在smartUpload将字节流转换成字符串时采用了系统默认的编码,而将Resin设置成服务后,系统默认的编码可能发生了改变,因此出现了乱码。后来,我们更改了smartUpload的源文件,增加了一个属性charset和setCharset(String)方法,将upload()方法中提取参数语句:
String value = new String(m_binArray, m_startData, (m_endData - m_startData) + 1 );
改成了
String value = new String(m_binArray, m_startData, (m_endData - m_startData) + 1, charset );
终于解决了这个乱码问题。
5.tomcat中乱码解决
在TOMCAT中,由Request.getParameter() 得到的参数,编码方式都是ISO8859_1。所以如果在浏览器输入框内输入一个汉字“英”,在服务器端就得到一个ISO8859_1编码的(0x00,0xD3,0x00,0xA2)。所以通常在接收参数时转码:
String wrongStr = response.getParameter(“name”);
String correctStr = new String(wrongStr.getBytes(“ISO8859_1”),”GB2312”);
在最新的SERVLET规范里面,也可以在获取参数之前执行如下代码:
request.setCharacterEncoding(“GB2312”);
中国网管论坛bbs.bitsCN.com
三、后记
接触Java和JSP已经有一年多了,这一年来最大的收获是越来越喜欢上了Java,开始把问题当作乐事去研究,没有了以前的恐惧心理,我相信我会继续坚持下去。这一年来,从网上学习了很多同行的宝贵经验,在此表示感谢。这是我第一篇自己总结的Java学习心得,由于水平有限,本文中偏颇和错误之处,欢迎指正。如果对你有些价值,在保留作者信息和文章原始出处的前提下可以随处转载。
撰写该文之前已参考了很多关于Java中文问题的文章,其中影响比较大的有owen1944在“Java研究组织”中发表的《这是我们公司总结的一些关于中文乱码问题的一些解决方案和经验和大家分享!》等。本文谈到的解决方法已应用到“基于网络的协作学习系统-WebCL”等项目中,并通过资源绑定的方式实现了该平台中文文两个版本的即时切换。Google根据浏览器自动选择语言,一个页面同时显示多种语言的国际化应用和车东的《Java中文处理学习笔记——Hello Unicode》一文引起了我极大的兴趣,日后想将继续探讨Java的国际化问题,欢迎大家一起讨论。
Home Page:
http://www.extremecomponents.org/
Extreme Table是最功能强大而又容易配置,扩展,自定义的Table 控件。
最紧要的功能包括排序, 分页, 导出Excel, pdf和汇总。
springside的best 示例代码在: listBook.jsp和listOrder.jsp.
基础读物:
1. ExtremeTable自带的文档 .
2.Luck翻译的ExtremeTable官方文档中文版
1. 安装
1.1 安装原始版本
1. 从http://www.extremecomponents.org/ 下载最新版
2.把extremecomponents.jar 放入web-inf/lib
3.根据minum, 导出excel 或 导出pdf的需要,把/lib下的jar copy到web-inf/lib
4.把images里的图片放入web的任意目录
5.把css文件放到web的任意目录
6.在/source/org/extremecomponents/table/core目录找到extremetable.properties文件,把它复制到WEB-INF/classes里面并进行修改.
7.修改web.xml (见2)。
0.2 复制SpringSide的样式
1.Copy springside的Table Head的图,查。
2.将springside中的修改版extremecomponents.css文件放入任意目录
3.把/src/resource/extremetable.properties 复制到对应目录
0.3 使用Compact View很重要
默认的式样不符合大家的审美,幸亏还有Compact View和css设置,让我们可以敬谢不敏它号称非常方便的HtmlView子类扩展。
1. 在全局设置里
table.view.html=org.extremecomponents.table.view.CompactView
2.把images里/table/compact/*.gif copy 到 /table
2.全局属性配置
ExtremeTable支持在properties文件里方便的统一配置丰富的全局属性
在/source/org/extremecomponents/table/core目录找到extremetable.properties文件,把它复制到WEB-INF/classes里面.
在web.xml里面增加
<context-param>
<param-name>extremecomponentsPreferencesLocation</param-name>
<param-value>/extremetable.properties</param-value>
</context-param>
一般只写需要修改的文件:
table.filterable=false
table.imagePath=/images/table/*.gif
table.locale=zh_CN
table.view.html=org.extremecomponents.table.view.CompactView
row.highlightRow=true
column.format.date=yyyy-MM-dd
column.format.currency=###,###,###,###,#00.00
3.导出Excel
注意Excel 导出的Tag已改为,且官方版不支持中文文件名。
<ec:exportXls fileName="BookList.xls" tooltip="导出 Excel"/>
在web.xml必须加入:
<filter>
<filter-name>eXtremeExport</filter-name>
<filter-class>org.extremecomponents.table.filter.ExportFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>eXtremeExport</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
ExtremeTable导出的一项重要特性是可以根据不同的View导出不同的列.
比如html管理界面可能只列出订单几个最重要的列,而导出excel时则需要导出所有的列。
<ec:column property="orderdate" title="订单日期" cell="date" format="yyyy-MM-dd" viewsDenied= "html"/>
又比如有些操作按纽的列只想在html出现,excel里不想出现:
<ec:column property="orderdate" title="订单日期" viewAllowed= "html"/>
以上订单日期列在html view看不到,而excel,pdf view可以见到.
而"编辑图标","选择Checkbox"这些管理界面上的列,也可以设置不export到pdf,excel view
4.其他要点
4.1. 使用排序功能时,如果property为嵌套属性时,需要设置alias
因为javascript的关系.
<ec:column property="customer.name" title="客户名称" alias="customer"/>
4.2. Date Cell与NumberCell 和RowCountCell
在全局属性定义:
column.format.date= yyyy-MM-dd
column.format.currency= ¥###,###,#00.00
<ec:column property="totalprice" title="总金额" cell="currency" width="80" />
<ec:column property="shipdate" title="发货日期" cell="date" width= "80"/>
<e
其中RowCountCell用于显示序号。
4.3. 汇总
在column中加入两个cac属性,就会自动在table的底端显示汇总
<ec:column property="totalprice" title="总金额" cell="number" format="###,###,##0.00" calc="total" calcTitle= "汇总:"/>
4.4 i18N
在Web.xml里面指定i18N的Properties文件
<context-param>
<param-name>extremecomponentsMessagesLocation</param-name>
<param-value>messages</param-value>
</context-param>
<ec:column property="name" title="book.name"/>
4.5数据库分页
EC默认从数据库中查找所有记录然后由它来分页,如果查询结果较大,可以考虑使用数据库端分页的方案。
此时,EC会使用Limit对象向Controller传递PageNo,PageSize,OrderBy等分页信息。而服务端将向EC返回总记录数和当前页的内容,具体请参看 BookController。
4.6不显示导出excel及分页
最快的方式是设置table的showStatusBar= false
<ec:table items= "order.orderItems"var="item" action="" showStatusBar= "false">
4.7ImagePath
旧版EC包括其文档都在每个table tag里定义ImagePath, 新版已经可以自动插入ContextPath,只需在properties文件定义table.imagePath=/images/table/*.gif即可
5.自定义及扩展
ExtremeTable比Display Tag的i一个就是良好的自定义和扩展能力,不用千人一面。
不过,人总是懒的,虽然扩展子类很容易,但能不扩展就还是不可扩展了。幸亏用Compact View再改改CSS一般就能达到要求。
5.1. 良好的CSS定义
ExtremeTable 的CSS定义非常严谨, 并且提供很多个层次的修改方式:
1.直接修改css文件, 可以把table view改成任意样式
2.修改全局属性, 改变某种元素对应的css class名
table.styleClass=tableRegion
3.在某个页面的row,column tag中定义class
<ec:row style="" or styleClass=""/>
SpringSide采用层次1, 直接修改extremecomponents.css为合适的样式
5.2方便的<tr><td>属性扩展
row与column已提供了丰富的普通属性与 javascript属性如onClick(), 如果属性仍然未够,可自行扩展<row>与<column>tag ,并通过全局属性指定新的子类名。
为了方便大家在子类扩展属性, extreme Table提供了专门的回调函数与Util函数如
public void addColumnAttributes(Column column) {
column.addAttribute("customAttributeOne", customAttributeOne);
}
详细请看手册,Table, Row,Column都提供相同的扩展:
http://extremecomponents.org/wiki/index.php/Column#Extended_Attributes
如果不是Extremetable的特别支持, 普通taglib class要扩展属性的话:
1.定义真正的Java属性,setter,getter
2. tld文件中声明新的属性
3.重载输出函数将新属性打印出来
5.3 扩展HTMLView,cell 等显示组件
Extreme Table在这些类的扩展上放便很多,但这个版本里我还没有进行尝试,就不写了。请自行参考官方文档:http://extremecomponents.org/wiki/index.php/Html_View_Tutorial .