Web应用乱码问题


Web应用乱码问题

简介

每个国家(或区域)都规定了本国家(或地区)计算机信息交换用的字符编码集,如美国的扩展ASCII码, 中国的GB2312-80,日本的JIS 等,作为该国家/区域内信息处理的基础,有着统一编码的重要作用。字符编码集按长度分为SBCS(单字节字符集),DBCS(双字节字符集)两大类。早期的软件(尤其是操作系统),为了解决本地字符信息的计算机处理,出现了各种本地化版本(L10N),为了区分,引进了LANG, CodePage等概念。但是由于各个本地字符集代码范围重叠,相互间信息交换困难;软件各个本地化版本独立维护成本较高。因此有必要将本地化工作中的共性抽取出来,作一致处理,将特别的本地化处理内容降低到最少。这也就是所谓的国际化(I18N)。各种语言信息被进一步规范为Locale 信息。处理的底层字符集变成了几乎包含了所有字形的Unicode。

现在大部分具有国际化特征的软件核心字符处理都是以Unicode 为基础的,在软件运行时根据当时的Locale/Lang/CodePage 设置确定相应的本地字符编码设置,并依此处理本地字符。在处理过程中需要实现Unicode 和本地字符集的相互转换,甚或以Unicode 为中间的两个不同本地字符集的相互转换。这种方式在网络环境下被进一步延伸,任何网络两端的字符信息也需要根据字符集的设置转换成可接受的内容。

Java语言内部是用Unicode表示字符的,遵守Unicode V2.0。Java程序无论是从/往文件系统以字符流读/写文件,还是往URL连接写HTML信息,或从URL连接读取参数值,都会有字符编码的转换。这样做虽然增加了编程的复杂度,容易引起混淆,但却是符合国际化的思想的。

从理论上来说,这些根据字符集设置而进行的字符转换不应该产生太多问题。而事实是由于应用程序的实际运行环境不同,Unicode和各个本地字符集的补充、完善,以及系统或应用程序实现的不规范,转码时出现的问题时时困扰着程序员和用户。

9.2  常见的中文编码问题及其解决方案

网上常出现的JSP编码问题一般都表现在客户端,如:浏览器中看到的 JSP页面中的汉字怎么都成了“?”; JSP页面无法显示GBK汉字;JSP页面中内嵌在<%...%>、<%=...%>等标签包含的Java代码中的中文成了乱码,但页面的其它汉字是正常显示的;JSP不能接收表单提交的汉字等。隐藏在这些问题后面的是各种错误的字符转换和处理。解决类似的字符编码问题,需要了解JSP的运行过程,检查可能出现问题的各个点。

运行于Java应用服务器的JSP为浏览器提供HTML内容,其过程如图9.1所示:

图9.1 JSP的运行过程

其中有字符编码转换的地方有:JSP编译。Java应用服务器将根据虚拟机的 “file.encoding”值读取JSP源文件,编译生成 Java源文件,再根据“file.encoding”的值写回文件系统。如果当前系统语言支持GBK,那么这时候不会出现编码问题。如果是英文的系统,如LANG是en_US的Linux、AIX或Solaris,则要将虚拟机的file.encoding 值置成GBK。系统语言如果是GB2312,则根据需要,确定要不要设置file.encoding,将 file.encoding设为GBK可以解决潜在的GBK字符乱码问题。

Java需要被编译为.class才能在虚拟机中执行,这个过程存在与上边一样的 file.encoding问题。从这里开始Servlet和JSP的运行就类似了,只不过Servlet的编译不是自动进行的。

Servlet需要将HTML页面内容转换为浏览器可接受的编码发送出去。依赖于各Java AP Server的实现方式,有的将查询浏览器的accept-charset和accept-language参数或以其它的方式确定编码,有的则不管。因此采用固定编码也许是最好的解决方法。对于中文网页,可在JSP中设置contentType=“text/html; charset=GB2312”;如果页面中有GBK字符,则设置为contentType=“text/html; charset=GBK”。我们推荐使用的编码是“UTF-8”。

9.3  页面显示非英文乱码问题举例

我们先来看看下面这段代码encoding.jsp,如例9.1所示。

<%@ page language="java" contentType="text/html" %>

<html>

<head>

<title>中文乱码问题</title>

</head>

<body>

<%

out.println("代码片段中的中文");

%>

欢迎光临!

</body>

</html>

例9.1

当我们执行这个JSP页面的时候,可以发现显示出来的都是乱码。如图9.1所示。

图9.1

我们来讲讲为什么会出现这种情况,首先,我们要知道我们的代码使用什么编码编写的,由于我们的编辑器eclipse已经设定了JSP页面用UTF-8格式来编写,通过图9.2可知。

图9.2

JSP页面转译成Servlet时,JSP的静态内容(Servlet中用out.write()输出的内容)是当成String字符串来转换的,因为我们没有设置JSP页面的pageEncoding属性,所以将使用String字符串的默认编码“ISO8859-1”来转换。这样一来,由于转换时使用的编码和代码的编辑编码不一致,当转译完成时,就出现了乱码。

我们看看转译后的Servlet的代码,其中静态内容中的汉字出现了乱码,如图9.3所示。

图9.3

因此,我们来设置JSP页面<%@page%>指令的pageEncoding属性,设置为“UTF-8”。代码如例9.2所示。

<%@ page language="java" contentType="text/html" pageEncoding="UTF-8"%>

<html>

<head>

<title>中文乱码问题</title>

</head>

<body>

<%

out.println("代码片段中的中文");

%>

欢迎光临!

</body>

</html>

例9.2

这是,我们转译得到的Servlet类代码如图9.4所示,可以看出,Servlet中的代码是正常的。

图9.4

注意:我们的代码中设置的ContentType只有“text/html”,而转换成的Servlet中的ContentType中确变成了“text/html;charset=UTF-8”。转换时把系统默认的页面显示编码追加上去了。

执行JSP的结果如图9.5。能够正常显示,因为ContentType设置的参数告诉了浏览器,用UTF-8来显示我们的页面。

图9.5

由上可知,设置<%@page%>指令中的contentType属性就是在转换成Servlet时执行response.setContentType()。而contentType中设置的charset就是告诉浏览器用什么编码来显示我们的页面,下面我们给我们的JSP页面代码设置这个charset,代码如例9.3所示。

<%@ page language="java" contentType="text/html;charset=BIG5" pageEncoding="UTF-8"%>

<html>

<head>

<title>中文乱码问题</title>

</head>

<body>

<%

out.println("代码片段中的中文");

%>

欢迎光临!

</body>

</html>

例9.3

我们通过阴影部分的代码告诉浏览器,用BIG5码来显示我们的页面。执行结果如图9.6所示,显示出来的是乱码。

图9.6

我们来看看现在浏览器中使用的编码,看以看到是Big5,如图9.7所示。

图9.7

因此,我们在这里要求contentType中的charset要和pageEncoding中设置的编码一致,才能保证页面的正常显示。

还有一点要提,就是HTML页面中可以通过如下代码来设置contentType。

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

在JSP页面中,如果我们设置了<%@page%>指令的contentType属性,则会覆盖<meta>标签设置的content属性,而<%@page%>属性在JSP页面中式不可少的,所以我们不在需要<meta>标签,因为它将不起作用,如果非要写上,最好和<%@page%>指令中的设置一致。有兴趣的读者可以自行实验。

9.4  页面间传递非英文参数乱码问题举例

我们还是从例子开始看起,首先我们做一个发送参数的JSP页面sendparam.jsp,在里面,我们设置两个表单,一个通过GET方法提交,另一个通过POST方法提交。代码如例9.4。

<%@ page language="java" contentType="text/html;   charset=UTF-8"

pageEncoding="UTF-8"%>

<html>

<head>

<title>参数传递乱码问题例_发送参数</title>

</head>

<body>

<table width="400" border="1">

<tr>

<td width="40%" align="right">GET方法的参数:</td>

<td width="60%" align="left">

<form action="showparam.jsp" method="get">

<input type="text" name="getparam"/>

<input type="submit" value="提交"/>

</form>

</td>

</tr>

<tr>

<td width="30%" align="right">POST方法的参数:</td>

<td width="70%" align="left">

<form action="showparam.jsp" method="post">

<input type="text" name="postparam"/>

<input type="submit" value="提交"/>

</form>

</td>

</tr>

</table>

</body>

</html>

例9.4

下面,我们来创建,显示参数的JSP页面showparam.jsp,代码如例9.5所示。

<%@ page language="java" contentType="text/html;   charset=UTF-8"

pageEncoding="UTF-8"%>

<html>

<head>

<title>参数传递乱码问题例_显示参数</title>

</head>

<body>

<%

// 显示参数

out.println(" GET方法的参数:" + request.getParameter("getparam"));

out.println("<br>");

out.println("POST方法的参数:" + request.getParameter("postparam"));

%>

</body>

</html>

例9.5

我们执行sendparam.jsp,我们看到如图9.8所示的页面。我们在GET方法参数的输入框中输入了“张三”,在POST方法参数的输入框中输入了“李四”。

图9.8

当我们点击了GET方法的提交按钮,得到如图9.9的页面。

图9.9

地址栏中显示的是汉字进行相当于加密后的值,其实产地的参数就是“张三”,而我们显示出来的确是乱码。点击POST方法的提交按钮后,我们同样得到的是乱码,如图9.10所示。

图9.10

为什么会出现乱码呢?

Servlet引擎实现request.getParameter()时,会先在缓存里找一下,如果没有,就会开始解析参数并把解析后的参数和其对应的值放入缓存中。而在解析参数时,Servlet规范中规定:首先按照 request.getCharacterEncoding()获取的编码对解析出来的byte进行编码,创建新的字符串。如果request.getCharacterEncoding()的值是null,则按照ISO8859-1来进行编码。而现在大多的浏览器在发送数据时都没有发送客户端的数据编码。所以在没有预先设置过的情况下,引擎应该都是按着ISO8859-1来处理参数的值的。

我们页面的编码是UTF-8,参数也是按照UTF-8来传递的,但是在showparam.jsp中获得参数确是用ISO8859-1来编码,当然会出现乱码。

那我们怎么来解决这个问题呢?我们这里提供三种方式来解决这个问题。下面我们会通过例子来说明这三种方法,我们每种修改的方法都是以例9.5为基础来修改。并且我们不会修改sendparam.jsp而只会修改showparam.jsp。而我们输入的参数也和上例一样GET方法是“张三”,POST方法是“李四”。

第一种,通过request.setCharacterEncoding()方法,我们修改代码如例9.6所示。

<%@ page language="java" contentType="text/html;   charset=UTF-8"

pageEncoding="UTF-8"%>

<html>

<head>

<title>参数传递乱码问题例_显示参数</title>

</head>

<body>

<%

// 设置获取参数用的编码

request.setCharacterEncoding("UTF-8");

// 显示参数

out.println(" GET方法的参数:" + request.getParameter("getparam"));

out.println("<br>");

out.println("POST方法的参数:" + request.getParameter("postparam"));

%>

</body>

</html>

例9.6

我们只是追加了如阴影部分的代码,GET方法的执行结果如图9.11所示,POST方法的执行结果如图9.12所示。

图9.11

图9.12

我们发现,通过这种方法只是对POST方法传递参数起了作用,对GET是没有作用的。

第二种方法,通过new String()方法来解决。我们修改showparam.jsp,代码如例9.7所示。

<%@ page language="java" contentType="text/html;   charset=UTF-8"

pageEncoding="UTF-8"%>

<html>

<head>

<title>参数传递乱码问题例_显示参数</title>

</head>

<body>

<%

// 获得参数

String   getParam = request.getParameter("getparam");

String postParam = request.getParameter("postparam");

// 转换参数

if (null != getParam) {

getParam = new String(getParam.getBytes("ISO8859-1"), "UTF-8");

}

if (null != postParam) {

postParam = new String(postParam.getBytes("ISO8859-1"),

"UTF-8");

}

// 显示参数

out.println(" GET方法的参数:" + getParam);

out.println("<br>");

out.println("POST方法的参数:" + postParam);

%>

</body>

</html>

例9.7

在这次修改中,我们首先获得参数,因为我们知道获得的参数是按照ISO8859-1来编码的,这样直接显示肯定是不行的,那么,我们就把ISO8859-1的字符串转换成UTF-8的字符串,理论上就应该能够正常显示了,我们通过例9.7中的阴影部分的代码来实现转码,现在,让我们来看看结果,GET方法的结果如图9.13,POST方法的结果如图9.14所示。

图9.13

图9.14

通过这个例子我们可以发现,第二种方法不管是GET还是POST都可以显示出正确的汉字。

下面我们来看看第三种方法,这个方法是修改Tomcat配置,我们在Tomcat的安装目录下找到/conf/ server.xml文件,我们打开它,在里边找到如图9.15所示的这段配置。

图9.15

由于Tomcat的默认端口是8080,所以,我们只需要修改这段配置,在其中加入如下配置URIEncoding="UTF-8",如图9.16所示。

图9.16

其他的代码我们都保持原来的样子不用修改,我们来重启Tomcat后,再来执行我们的画面,GET方法的结果如图9.17,POST方法的结果如图9.18所示。

图9.17

图9.18

我们发现,这种方法只是对GET方式传递参数有效而对POST方式传递参数无效。这个方法虽然简单,但是需要改动的地方是服务器软件级别的,如果稍微变动系统将无法正确转码,移植性不高下面我们列表来总结一下这三种方法的效果。

方法

GET方式传参

POST方式传参

request.setCharacterEncoding()设置传参编码

无效

有效

new   String()进行转码

有效

有效

修改Tomcat配置文件

有效

无效

在有的情况下,只使用一种方法是不能完全解决中文乱码问题,这是,我们需要综合使用这几种方法来解决,根据需要使用对应的方法,才能解决问题。

9.5  数据库操作中非英文乱码问题举例

对于数据库操作乱码的问题,不管是DB2、Oracle还是MySql,只是需要注意几个点就能够保证使用数据库不出现乱码现象。

最需要注意的是在安装数据库的时候指定好数据库使用的字符集。如下图所示,我们在安装好MySql后会对其进行设置,在这里我们设置的是UTF-8。

然后是对于某些数据库,在建立数据库的时候也要注意指定的字符集。下图演示的是MySql中建立数据库时制定字符集。

对于DB2,我们可以建库时使用如下语句来设置数据库级别的字符集。我们创建指定区域为中国(CN)的UTF-8数据库

CREATE DATABASE   dbname USING CODESET UTF-8 TERRITORY CN

通过上面的设置,一般的数据库的乱码问题基本上可以解决了。

对于MySql,我们还可以对其数据域进行设置字符集。如下图。

除了上边的设置,MySql还可以在连接数据库是制定使用的编码。即使用如下URL:

jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8

这样基本上就不会出现数据库操作乱码问题了。

9.6  总结

  • 我们可以设置<%@page %>指令的pageEncoding属性来设置页面转译时的编码,通过设置contentType属性中的charset来设置浏览器用什么编码来展示JSP页面。
  • JSP页面设置了<%@page %>指令后不需要设置HTML的<meta>标签。
  • 我们通过三种方式来获得正确的参数: request.setCharacterEncoding()设置传参编码;通过new String()来转码;修改Tomcat的Server.xml文件。

17Web应用乱码问题的更多相关文章

  1. java web 学习十(HttpServletRequest对象1)

    一.HttpServletRequest介绍 HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象 ...

  2. win10 环境 gitbash 显示中文乱码问题处理

    gitbash 是 windows 环境下非常好用的命令行终端,可以模拟一下linux下的命令如ls / mkdir 等等,如果使用过程中遇到中文显示不完整或乱码的情况,多半是因为编码问题导致的,修改 ...

  3. java中文乱码解决之道(一)-----认识字符集

    沉寂了许久(大概有三个多月了吧),LZ"按捺不住"开始写博了! java编码中的中文问题是一个老生常谈的问题了,每次遇到中文乱码LZ要么是按照以前的经验修改,要么则是baidu.c ...

  4. Sublime Text 3中文乱码解决方法以及安装包管理器方法

    一般出现乱码是因为文本采用了GBK编码格式,Sublime Text默认不支持GBK编码. 安装包管理器 简单安装 使用Ctrl+`快捷键或者通过View->Show Console菜单打开命令 ...

  5. java字符乱码

    在java中处理字符时,经常会发生乱码,而主要出现的地方在读取文本文件时发生,或者是写入到文件中,在其他地方打开乱码. 如下例子: BufferedReader br = null; try { br ...

  6. MAC下 mysql不能插入中文和中文乱码的问题总结

    MAC下 mysql不能插入中文和中文乱码的问题总结 前言 本文中所提到的问题解决方案,都是基于mac环境下的,但其他环境,比如windows应该也适用. 问题描述 本文解决下边两个问题: 往mysq ...

  7. ThinkPHP+Smarty模板中截取包含中英文混合的字符串乱码的解决方案

    好几天没写博客了,其实有好多需要总结的,因为最近一直在忙着做项目,但是困惑了几天的Smarty模板中截取包含中英文混合的字符串乱码的问题,终于解决了,所以记录下来,需要的朋友看一下: 出现乱码的原因: ...

  8. 【云知道】究极秒杀Loadrunner乱码

    Loadrunner乱码一击必杀 之前有介绍一些简单的针对Loadrunner脚本或者调试输出内容中乱码的一些设置,但是并没能完全解决一些小伙伴的问题,因为那些设置实在能力有限,还是有很多做不到的事情 ...

  9. 新手学习web遇到的一些乱码问题

    在新手学习web网站学习的时候经常会遇到?????这种乱码,对于刚起步的菜鸟来说真的很头痛,很容易打击继续学的信心当然了对于菜鸟的我最近也遇到过乱码问题,沉浸其中不能自拔,爱的深啊!!!!!我所遇到的 ...

随机推荐

  1. 剑指offer面试题24-二叉搜索树的后序遍历序列

    题目: /*  * 输入一个整数数组,推断该数组是不是某二叉搜索树的兴许遍历的结果.<br/>  * 假设是则返回true,否则返回false.<br/>  * 如果输入的数组 ...

  2. C#项目的生成事件及批处理文件

    一个C#项目,如果为同一个解决方案的其他项目所引用,则其编译后,会将DLL拷贝到引用项目中:但如果它并不被其他项目引用,但又想编译后能够自动将生成的东西拷贝过去,可以在项目的生成事件中,写上一些批处理 ...

  3. 使用Hibernate防止SQL注入的方法

    之前写代码,往后台传入一个组织好的String类型的Hql或者Sql语句,去执行. 这样其实是很蠢的一种做法!!!! 举个栗子~~ 我们模仿一下用户登录的场景: 常见的做法是将前台获取到的用户名和密码 ...

  4. 什么是sibling and tail recursive calls

    1 tail call 在函数f中调用函数b,如果这个调用是函数f中执行的最后一条指令,那么这个调用就称为tail call. 例子: int foo(float a, float b) { ... ...

  5. css3 3d特效汇总

    本篇全是实战,没有基础,如果不明白3d特效的原理,可能会看不懂,不过没关系,给你推荐一下 张鑫旭css3 3d转换,或者看我的另一篇博客  css3 2d转换3d转换以及动画的知识点汇总,看完这些3d ...

  6. 【174】C#添加非默认字体

    参考:C# WinForm程序安装字体或直接调用非注册字体 参考:百度知道 在Debug文件夹下面新建一个font的文件夹,然后将字体的文件复制到里面,使用的时候,直接调用字体文件! private ...

  7. SQL Server 方言类型映射问题

    关于SQL Server的类型映射问题,例如,nvarchar无法进行hibernate类型映射,需要通过convert进行类型转换方可进行获取

  8. bzoj 1672: [Usaco2005 Dec]Cleaning Shifts 清理牛棚【dp+线段树】

    设f[i]为i时刻最小花费 把牛按l升序排列,每头牛能用f[l[i]-1]+c[i]更新(l[i],r[i])的区间min,所以用线段树维护f,用排完序的每头牛来更新,最后查询E点即可 #includ ...

  9. 双栈排序 2008年NOIP全国联赛提高组(二分图染色)

    双栈排序 2008年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 大师 Master     题目描述 Description Tom最近在研究一个有 ...

  10. Hdu 4738 Caocao's Bridges (连通图+桥)

    题目链接: Hdu 4738 Caocao's Bridges 题目描述: 有n个岛屿,m个桥,问是否可以去掉一个花费最小的桥,使得岛屿边的不连通? 解题思路: 去掉一个边使得岛屿不连通,那么去掉的这 ...