当前位置: 首页 > news >正文

Java开发,表单提交中发生中文乱码的问题。

Web开发的中文问题一直困惑大家,尤其是对于初上手者。这次有机会彻底解决研究了一下中文乱码的原因和解决方案,做个总结。
为什么会有中文乱码?
因为在默认情况下,HTTP的包都是以“8859_1”来编码的(没办法,谁叫这些标准都是老美定的)。“8859_1”是西文编码方式,对于英文字母没有任何问题,但是对于中文就不行了。所以,如果不做任何设定,直接将中文用“8859_1”来编码传递,那结果必然是乱码。
解决思路是什么?
好在老美还是有国际化眼光的,HTTP包的编码方式可以由用户指定。因此,只要事先指定好用相对应的编码方式来对传递内容(比如表单提交的中文等)进行编码,就可以顺利解决乱码的问题。
两个基本概念
在进入具体的解决方法之前,首先要对两个基本概念作一下解释。对于由表单提交的内容,HTTP有两种传递方式,分别是“GET”方式和“POST”方式。
“GET”方式就是将各参数直接通过HTTP的包头(head)来传递,简而言之就是直接通过我们所熟悉的网址(URL)来传递,所以我们经常能看到的在一个网址后面跟着许多复杂的由“?”和“&”构成的字符串,其实这就是需要传递的参数了。
“POST”方式则是将所需传递的参数包在HTTP的正文(body)中来传递。因此通过“POST”方式来进行传递,在浏览器的网址上面什么都看不见。
因此,相比较而言,“POST”隐蔽性较好;而“GET”方式使用起来比较容易,直接写URL就可以了。
综上所述,不难发现,解决中文乱码问题实际上就变为对这两种HTTP传递的编码方式进行适当的设定。当然,从解决问题的难易以及对系统架构的完美性角度着手,又分为以下三个层次:
1)入门方法,在所有的servlet和jsp中堆设定用的代码。
2)中级方法,对web伺服器进行配置。
3)高级方法,编写filter过滤器,对“POST”和“GET”独立过滤处理。
下面就具体描述各解决方法:
1)入门方法,在所有的servlet和jsp中“堆”写设定用的代码。
所谓入门方法,那就是现实十分简单,当然效果也是很好的。只是必须在每个相应的文件中写相同的设定代码,代码的重复性就比较大。
由前面所述,由于“POST”和“GET”方式的不同,因此对应着两种的设定方式也不同。
“POST”的情况下,如果服务器端脚本是一个servlet,那只要在doPost()方法里面插入一句

request.setCharacterEncode("GB2312");

需要注意的是,这句设定必须在所有从request对象做提取操作之前执行,如果类似于request.getParameter()的操作在前,那么系统将使用默认的“8859_1”编码方式,而忽略后面的设定代码。
如果服务器端是一个jsp脚本,那只要在该脚本的jsp申明部分做好设定即可:

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

如果是“GET”方式,也就是想通过URL来传递中文的话,稍微要麻烦些,首先因为浏览器地址栏是不支持中文的,也就是如果直接将中文放置在超级连接里面是无效的。因此需要在发送端对中文内容进行编码,比如:

URLEncoder.encoder("http://localhost/submit?name=张三","UTF-8");

“UTF-8”表示用这种编码方式对原字符串进行编码,编码好之后看到的结果是

http://localhost/submit?name=%D5%C5%C8%FD

所以我们经常看到在浏览器里面有众多的类似与“%D5%C5%C8%FD”这样的字符串,就是表明被UTF-8编码过了。由于UTF-8是跨各种平台的通用编码方式,因此比较常用于各种语言文字的传输载体。
相对应的,在接受方需要进行反向的解码即可,代码如下:

new String( request.getParameter("name").getBytes("8859_1"), "gb2312" );

这里可能会有一些疑问,为什么用“8859_1”来解码。事实上,我在第一次尝试的时候也曾使用“UTF-8”来尝试解码,结果出现乱码失败。究其原因,尽管“张三”被编码成了“%D5%C5%C8%FD”来传输,但是在传输过程中,“%D5%C5%C8%FD”仍旧需要由“8859_1”来编码打包成HTTP,因此,在接收端,自然先需要由“8859_1”来还原到“%D5%C5%C8%FD”的“UTF-8”格式,然后再由“UTF-8”还原到“GB2312”。
所以这样也不难理解为什么所谓“浏览器地址栏是不支持中文”,不能直接用中文而要用“UTF-8”来通过“8859_1”来打包了,原因就是“%D5%C5%C8%FD”这串类似于密码般的字符串本身就是西文字符,用“8859_1”编解码没有任何问题。而中文由于是2byte一个汉字,直接用西文方式来编解码自然就会出现问题。这也就是为什么称“UTF-8”为“跨各种平台的通用编码方式”了。
背景小资料:
由于“UTF-8”是通用编码方式,因此所有的语言格式均可以转换为“UTF-8”,在日益国际的今天,多语言的系统要求越来越多,因此强烈建议使用“UTF-8”来做为系统统一的编解码方式,从而彻底解决中文乱码的问题。
“UTF-8”为了能做到兼容所有语言的编解码,因此每一个字符均用2个byte来编码。这样就造成了存西文字符时需要多一倍的空间。这也算是为了通用而付出的代价了。
2)中级方法,对web伺服器进行配置
可想而知,相对于“堆”写大量代码,配置一下web伺服器config文件来解决中文乱码问题就显得优雅许多。但是由于各种web伺服器的情况不同,其配置方法也不尽相同。因此,其兼容性是个比较大的问题。
这里列举一下,如何通过修改Tomcat的conf配置文件来解决中文乱码的问题。
找到Tomcat的配置文件server.xml中的Connector这一行,为其添加一个如下的属性

URIEncoding = "GB2312"

这样就指定了使用“GB2312”来进行编解码。不过需要注意的是,tomcat4.x以以前的版本由于蒋“POST”和“GET”等同视之,因此这样一句设定就可以适用于两种方法。而到了tomcat5.x以后,两种方式就分开处理了。因此在tomcat5.x的情况下,只做这个设定,那仅仅对“POST”方式有效,“GET”方式仍然会得到乱码。
不过好在tomcat5.x考虑到了这个问题,提供了一个附加的参数:

useBodyEncodingForURI = "true"

如果做了这样的设定,那5.x就将兼容4.x而“POST”和“GET”等同视之。
3)高级方法,编写filter过滤器,对“POST”和“GET”独立过滤处理。
高级方法,顾名思义,就是可以脱离于任何平台,同时又免去冗余的队旗代码工作的解决方案——编写过滤器,Filter。
首先编写一个过滤器SetCharacterEncodingFilter

public class SetCharacterEncodingFilter implements Filter {
/**
        * The default character encoding to set for requests that pass through
        * this filter.
        */
protected String encoding = null;
/**
        * The filter configuration object we are associated with.  If this value
        * is null, this filter instance is not currently configured.
        */
   protected FilterConfig filterConfig = null;
   /**
       * Should a character encoding specified by the client be ignored?
       */    
protected boolean ignore = true;
 // --------------------------------------------------------- Public Methods
   /**
    * Take this filter out of service.
    */
   public void destroy() {
       this.encoding = null;
       this.filterConfig = null;
   }
/**
    * Select and set (if specified) the character encoding to be used to
    * interpret request parameters for this request.
    *
    * @param request The servlet request we are processing
    * @param result The servlet response we are creating
    * @param chain The filter chain we are processing
    *
    * @exception IOException if an input/output error occurs
    * @exception ServletException if a servlet error occurs
    */
   public void doFilter(ServletRequest request, ServletResponse response,
                        FilterChain chain)
   throws IOException, ServletException {
 // Conditionally select and set the character encoding to be used
       if (ignore || (request.getCharacterEncoding() == null)) {
           String encoding = selectEncoding(request);
           if(encoding != null){
               HttpServletRequest httpServletRequest = (HttpServletRequest) request;
               if(httpServletRequest.getMethod().toLowerCase().equals("post")){
   //如果是POST方法
                   request.setCharacterEncoding(encoding);
               }
               else{
                 //如果是GET方法
                   //非常抱歉,我还有没有找到很好的对应get方法的代码
                   //一旦完成了这部分代码,马上添加在这里。
                   //!·#¥%……—*()——+|
               }
           }
       }
 // Pass control on to the next filter
       chain.doFilter(request, response);
   }
   /**
    * Place this filter into service.
    *
    * @param filterConfig The filter configuration object
    */
   public void init(FilterConfig filterConfig) throws ServletException {
   this.filterConfig = filterConfig;
       this.encoding = filterConfig.getInitParameter("encoding");
       String value = filterConfig.getInitParameter("ignore");
       if (value == null)
           this.ignore = true;
       else if (value.equalsIgnoreCase("true"))
           this.ignore = true;
       else if (value.equalsIgnoreCase("yes"))
           this.ignore = true;
       else
           this.ignore = false;
   }
 // ------------------------------------------------------ Protected Methods
   /**
    * Select an appropriate character encoding to be used, based on the
    * characteristics of the current request and/or filter initialization
    * parameters.  If no character encoding should be set, return
    * <code>null</code>.
    * <p>
    * The default implementation unconditionally returns the value configured
    * by the <strong>encoding</strong> initialization parameter for this
    * filter.
    *
    * @param request The servlet request we are processing
    */
   protected String selectEncoding(ServletRequest request) {
       return (this.encoding);
   }
}

编写完过滤器以后,需要对其进行部署,也就是在web.xml中做个配置:
在<display-name>标签之后,添加:

   <filter>
       <filter-name>Set Character Encoding</filter-name>
       <filter-class>com.zavax.utility.filters.SetCharacterEncodingFilter</filter-class>
           <init-param>
               <param-name>encoding</param-name>
               <param-value>UTF8</param-value>
           </init-param>
           <init-param>
               <param-name>ignore</param-name>
               <param-value>true</param-value>
           </init-param>
       </filter>
   <filter-mapping>
       <filter-name>Set Character Encoding</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>

转载于:https://www.cnblogs.com/pony/archive/2008/08/05/1260925.html

相关文章:

  • 吐血推荐——(一级集成资质投标文件)绝密!!
  • 删除链表的倒数第N个节点
  • 计算机网络安全《社会工程学》——欺骗的艺术
  • Docker 容器的运行(八)
  • 刘翔那点事
  • 大家一起学CCNP BSCI路由
  • 数据结构开发(18):归并排序和快速排序
  • 工程文档管理
  • http和Https简介、详解
  • System.IO.StreamWriter写UTF-8文件取消写入BOM
  • asp.net core MVC 控制器,接收参数,数据绑定
  • Web站点风格切换的实现
  • 对类的理解(c++)
  • JS实现购物车01
  • 重装Linux也不用重新配置的方法
  • (三)从jvm层面了解线程的启动和停止
  • 【React系列】如何构建React应用程序
  • 【跃迁之路】【585天】程序员高效学习方法论探索系列(实验阶段342-2018.09.13)...
  • AHK 中 = 和 == 等比较运算符的用法
  • avalon2.2的VM生成过程
  • JavaScript-Array类型
  • Laravel核心解读--Facades
  • magento2项目上线注意事项
  • markdown编辑器简评
  • PV统计优化设计
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • react-native 安卓真机环境搭建
  • React的组件模式
  • Vue 2.3、2.4 知识点小结
  • 京东美团研发面经
  • 聊一聊前端的监控
  • 数据可视化之 Sankey 桑基图的实现
  • 算法-图和图算法
  • 以太坊客户端Geth命令参数详解
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • #大学#套接字
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (1)Nginx简介和安装教程
  • (2)STL算法之元素计数
  • (2015)JS ES6 必知的十个 特性
  • (3)(3.5) 遥测无线电区域条例
  • (delphi11最新学习资料) Object Pascal 学习笔记---第5章第5节(delphi中的指针)
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (南京观海微电子)——COF介绍
  • .bat批处理(一):@echo off
  • .Mobi域名介绍
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .NET CORE Aws S3 使用
  • .NET Core 实现 Redis 批量查询指定格式的Key