简单应用系统中的中文编码
也许读者会问,这些跟我的中文问题有什么关系?我从来没有让我的程序使用过UTF-8或者那些古怪的字符串编码。
遗憾的是,每个程序员确实需要关心这些东西。不管你愿意不愿意,一个简单的java应用,已经在你并没有注意地情况下,使用了以上的很多编码。而这些编码过程在程序的处理过程中,就必须要经过很多次的转换,任何一个转换的错误,都可能是中文问题的根源!
一个玩具性质的JSP应用,就已经使用了GB2312,Unicode,UTF-8,还有这些字符串所转换出来的各种形式。一个普通的J2EE应用,涉及的编码更是繁多。
让我们来看一下在一个最简单的JSP页面中,“中文”两个字的各种表现形式。必须要说明一点的是,只有在应用程序完全按照tomcat的推荐方法设置的时候,以下的字符才会是这种形式,任何一个参数的不同,都将造成结果的不同。
3. 字符转换过程
我们已经看到,在一个过于简单的系统中,已经有3种以上的中文编码,繁多的字符串编码模式,组合出各种奇怪的数据。
而所有这些在程序中运转的时候,都会涉及很多转换的过程。
举简单的例子(以下的编码只是举例,并不是说在这个环节不能使用其他的编码):
1. 用户的输入是GB2312,java需要读取成Unicode的字符串
2. 如果在java代码中写了中文,java编译的类文件会用UTF-8来保存
3. 配置文件使用了java的编码模式(\ u4e2d)
4. 数据序列化XML的形式时,会把字符串转换成XML的字符编码(中)
读取的时候当然是相反的过程
Java WEB开发中的中文乱码问题解决
本文对Java JSP/SERVLET开发中的比较常见的中文参数乱码 问题做一个透彻地分析,并试图通过范例帮助大家能轻松理解,同时给出Java WEB中文参数乱码解决的几种方法。
AD:
本文所有范例以UTF-8为例。大家可以根据自己的情况加以替换。
在开始本文之前,假设本文的读者已经熟悉或了解以下技术:
- Java语法
- Java WEB开发的基本概念
- Jsp
- Servlet
- 至少一种支持JSP/SERVLET的Web服务器(包括安装,运行)
浏览器/WEB服务器之间的参数传递原理分析
浏览器/WEB服务器之间的中文参数传递
1,表单(form)中文参数的传递方法。我们使用一个简单的范例说明表单提交时浏览器的对中文参数的处理。
SubmitAsia.html
view plaincopy to clipboardprint?
使用任意浏览器打开该文件,在输入框内输入 “你好” 中文2字,然后按submit按钮,我们注意到浏览器的地址栏:
file:///C:/SubmitAsia.html?userName=%E4%BD%A0%E5%A5%BD
刚才输入“你好”二字,被转换为 %E4%BD%A0%E5%A5%BD 后被发往服务器。
这个 %E4%BD%A0%E5%A5%BD 是什么呢?
我们先使用一个Java程序来测试一下。如下:
EnDecoderUtil.java
view plaincopy to clipboardprint?
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class EnDecoderUtil {
public static void main(String []args) {
try {
String str = URLEncoder.encode("你好", "UTF-8");
System.out.println(str);
str = URLDecoder.decode(str, "UTF-8");
System.out.println(str);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class EnDecoderUtil {
public static void main(String []args) {
try {
String str = URLEncoder.encode("你好", "UTF-8");
System.out.println(str);
str = URLDecoder.decode(str, "UTF-8");
System.out.println(str);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
编译执行:
c:\>javac EnDecoderUtil.java
c:\>java EnDecoderUtil
%E4%BD%A0%E5%A5%BD
你好
我们发现,浏览器发送给服务器的“你好”中文参数跟使用java.net.URLEncoder.encode()方法编码后的值完全一样。
原来,浏览器在向服务器传递参数时,对于非数字,非英文的字符(比如中日韩文)时,会先将其加以变换(编码),再发送给服务器,服务器接收到这种格式的字符时,会将其反向编码,还原成原来的字符。
浏览器/Java WEB服务器之间的中文参数传递 过程模拟
为了帮助大家能更好地理解,我们使用下面的例子,该例通过联结的形式向Google服务器发送一个查询命令参数。
比如,我们通过Google查询“你好啊”,通过以下2种方法向Google服务器发送参数:
SubmitAsia2Google.html
view plaincopy to clipboardprint?
方法1:你好啊
方法2:你好啊
方法1:你好啊
方法2:你好啊
使用任意浏览器打开该文件。
方法1:你好啊
方法2:你好啊
使用方法1时,Google的查询页面通常会显示乱码,方法2时显示完全正常。
通过这个例子,我们知道,为了让服务器能够正常接收中文参数,对HTML页面的中文参数一定要经过编码处理。
表单里的中文字符在提交时,浏览器已经替我们做了编码处理,但联结()里的中文需要我们自己处理。
JSP页面联结的中文参数编码方法
JSP页面里的联结很多情况下是动态生成的,比如根据数据库里的数据的不同动态生成包含中文关键字的联结等等。
方法1:JSP里直接使用java.net.URLEncoder.encode()。例:">联结
方法2:Javabean使用java.net.URLEncoder.encode()
在Javabean里使用java.net.URLEncoder.encode()处理之后,JSP里加以引用。
view plaincopy to clipboardprint?
...
<%
String chars = myBean.getSomeProp();
out.println("联结");
%>
...
...
<%
String chars = myBean.getSomeProp();
out.println("联结");
%>
...
方法3:使用自定义标签。
在自定义标签里使用java.net.URLEncoder.encode()方法处理。
关于自定义标签的具体方法,这里不做介绍。
JSP与SERVLET的连动
JSP经过上面的处理之后,最后输出的HTML页面联结已经可以正常向服务器传递中文参数了。
下面我们阐述一下Servlet里怎么接收/解析中文参数。
对于你好之类的联结,我们可以用下面的servlet来解析传递过来的中文参数。
GetAsiaCharServlet.java
view plaincopy to clipboardprint?
import java.io.IOException;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class GetAsiaCharServlet extends HttpServlet {
@Override
//redir?key=xxxx
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String key = req.getParameter("key");
key = new String(key.getBytes("ISO-8859-1", "utf-8"));
System.out.println(keyword);
//...
//重定向处理
//res.sendRedirect("http://www.google.com/search?q="+URLEncoder.encode(key, "utf-8"));
}
}
import java.io.IOException;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class GetAsiaCharServlet extends HttpServlet {
@Override
//redir?key=xxxx
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String key = req.getParameter("key");
key = new String(key.getBytes("ISO-8859-1", "utf-8"));
System.out.println(keyword);
//...
//重定向处理
//res.sendRedirect("http://www.google.com/search?q="+URLEncoder.encode(key, "utf-8"));
}
}
我们注意到使用req.getParameter("key")得到参数后,还使用了new String(key.getBytes("ISO-8859-1", "utf-8"))把ISO-8859-1字符集形式转换成UTF-8形式。
为什么呢?因为iso-8859-1是Java中网络传输使用的标准字符集,req.getParameter("key")得到的还是ISO-8859-1字符集,所以要转换一下才不会是乱码。
最后,顺便提一下,采用servlet重定向时,也需要对包含中文文字的参数做特殊处理。
例如,SERVLET从HTML页面的联结接受参数,然后重新定向到Google搜索。则可以在上面的GetAsiaCharServlet里加上如下处理:res.sendRedirect("http://www.google.com/search?q="+URLEncoder.encode(key, "utf-8"));
也就是说,需要把参数取出来,转换,再重新使用URLEncoder.encode编码,这样就不会出现乱码现象。
关于JavaWeb技术开发中中文乱码问题的深入探讨
摘要:在利用Servalete/Jsp技术开发Web应用程序的时候,不可避免的会遇到中文乱码问题。如:页面编码不一致;表单get提交时产生乱码问题;表单使用post方式时产生的乱码问题等。本文首先详细分析这些中文乱码问题产生的原因,并以图例清晰具体的展现出来,最后在此基础上提出详细合理的解决方案。
关键字:JavaWeb技术开发中文乱码字符集
1.引言
为了能在计算机中表示不同语言中字符,每个国家(或区域)都规定了计算机信息交换用的字符编码集,如美国的ASCII,中国GB2312和GBK,日本的JIS等。
Java语言内部用Unicode来表示字符,Unicode被称为统一的字符编码标准集,它为几乎每种语言中的字符设定了唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。这也使得Java具有良好的可移植性,符合其国际化的思想。
然而在实际应用中,由于应用程序的运行环境不同,和各个本地字符集的补充、完善,以及系统或应用程序实现的不规范,转码时出现的中文乱码问题时时困扰着程序员和用户。
2.中文乱码产生的原因
Java中文问题一直困扰着很多初学者,如果了解了Java系统的中文问题原理,我们就可以对中文问题能够采取根本的解决之道。
最古老的解决方案是使用String的字节码转换,这种方案问题是不方便,我们需要破坏对象封装性,进行字节码转换。
还有一种方式是对J2EE容器进行编码设置,如果J2EE应用系统脱离该容器,则会发生乱码,而且指定容器配置不符合J2EE应用和容器分离的原则。
在Java内部运算中,涉及到的所有字符串都会被转化为UTF-8编码来进行运算。那么,在被Java转化之前,字符串是什么样的字符集?Java总是根据操作系统的默认编码字符集来决定字符串的初始编码,而且Java系统的输入和输出的都是采取操作系统的默认编码。
因此,如果能统一Java系统的输入、输出和操作系统3者的编码字符集合,将能够使Java系统正确处理和显示汉字。这是处理Java系统汉字的一个原则,但是在实际项目中,能够正确抓住和控制住Java系统的输入和输出部分是比较难的。J2EE中,由于涉及到外部浏览器和数据库等,所以中文问题乱码显得非常突出。
J2EE应用程序是运行在J2EE容器中。在这个系统中,输入途径有很多种:一种是通过页面表单打包成请求(request)发往服务器的;第二种是通过数据库读入;还有第3种输入比较复杂,JSP在第一次运行时总是被编译成Servlet,JSP中常常包含中文字符,那么编译使用javac时,Java将根据默认的操作系统编码作为初始编码。除非特别指定,如在Jbuilder/eclipse中可以指定默认的字符集。
输出途径也有几种:第一种是JSP页面的输出。由于JSP页面已经被编译成Servlet,那么在输出时,也将根据操作系统的默认编码来选择输出编码,除非指定输出编码方式;还有输出途径是数据库,将字符串输出到数据库。
由此看来,一个J2EE系统的输入输出是非常复杂,而且是动态变化的,而Java是跨平台运行的,在实际编译和运行中,都可能涉及到不同的操作系统,如果任由Java自由根据操作系统来决定输入输出的编码字符集,这将不可控制地出现乱码。
正是由于Java的跨平台特性,使得字符集问题必须由具体系统来统一解决,所以在一个Java应用系统中,解决中文乱码的根本办法是明确指定整个应用系统统一字符集。
指定统一字符集时,到底是指定ISO8859_1、GBK还是UTF-8呢?
(1)如统一指定为ISO8859_1,因为目前大多数软件都是西方人编制的,他们默认的字符集就是ISO8859_1,包括操作系统Linux和数据库MySQL等。这样,如果指定Jive统一编码为ISO8859_1,那么就有下面3个环节必须把握:
A开发和编译代码时指定字符集为ISO8859_1。
B运行操作系统的默认编码必须是ISO8859_1,如Linux。
使用语言关键字,而非类型引用的框架类型名称
本文内容属性值
规则 ID
IDE0049
标题
使用语言关键字,而非类型引用的框架类型名称
类别
Style
Subcategory
语言规则(语言关键字而非框架类型名称)
适用的语言
C# 和 Visual Basic
选项
dotnet_style_predefined_type_for_locals_parameters_members
dotnet_style_predefined_type_for_member_access
概述
此规则涉及语言关键字 where they exist 的使用,而不涉及框架类型名称的使用。
注意
即使你在 时启用代码样式规则,也不会启用此规则。 它仅在 Visual Studio 编辑器中浮出水面。
选项
使用此规则的关联选项将其应用于:
选项值为 true 表示,如果类型中有用于表示类型的关键字,则首选语言关键字(例如 int 或 Integer),而不是类型名称(例如 Int32)。 值为 false 代表类型名称(而非语言关键字)为首选项。
若要了解如何配置选项,请参阅。
dotnet_style_predefined_type_for_locals_parameters_members属性价值说明
选项名称
dotnet_style_predefined_type_for_locals_parameters_members
选项值
true
局部变量、方法参数和类成员的语言关键字为首选项
false
局部变量、方法参数和类成员的类型名称为首选项
默认选项值
true
// dotnet_style_predefined_type_for_locals_parameters_members = true
private int _member;
// dotnet_style_predefined_type_for_locals_parameters_members = false
private Int32 _member;
' dotnet_style_predefined_type_for_locals_parameters_members = true
Private _member As Integer
' dotnet_style_predefined_type_for_locals_parameters_members = false
Private _member As Int32
dotnet_style_predefined_type_for_member_access属性价值说明
选项名称
dotnet_style_predefined_type_for_member_access
选项值
true
成员访问表达式的语言关键字为首选项
false
成员访问表达式的类型名称为首选项
默认选项值
true
// dotnet_style_predefined_type_for_member_access = true
var local = int.MaxValue;
// dotnet_style_predefined_type_for_member_access = false
var local = Int32.MaxValue;
' dotnet_style_predefined_type_for_member_access = true
Dim local = Integer.MaxValue
' dotnet_style_predefined_type_for_member_access = false
Dim local = Int32.MaxValue
抑制警告
如果只想抑制单个冲突,请将预处理器指令添加到源文件以禁用该规则,然后重新启用该规则。
#pragma warning disable IDE0049
// The code that's violating the rule is on this line.
#pragma warning restore IDE0049
若要对文件、文件夹或项目禁用该规则,请在配置文件中将其严重性设置为 none。
[*.{cs,vb}]
dotnet_diagnostic.IDE0049.severity = none
若要禁用所有代码样式规则,请在配置文件中将类别 Style 的严重性设置为 none。
[*.{cs,vb}]
dotnet_analyzer_diagnostic.category-Style.severity = none
有关详细信息,请参阅如何禁止显示代码分析警告。
另请参阅