中文化和国际化问题权威解析之四:Java中文化和国际化攻略
2009-08-11 20:09
309 查看
一般攻略
既然在Java
内部是直接使用
Unicode
表示一切字符的,表达中文自然不成问题。因此所谓的中文问题并不是由
Java
本身引起的。而是因为对
Java
和
Unicode
理解不透或应用不当引起的。下面列出的原则,是解决一切中文问题的总纲:
在
Java
内部,正确使用
Unicode
标准。对于中文来说,每个汉字使用一个
char
表示。
在所有的输入输出环节,指明正确的编码方式,进行正确的字符到字节,或字节到字符的转换。
如果输入源或输出目标直接支持,尽可能直接使用
Unicode
进行输入输出。例如,
Oracle
数据库直接支持
UTF-8
的文本数据。使用
UTF-8
操作
Oracle
,可自动兼容所有的语言文字;反之,使用
ISO-8859-1
或者
ASCII
去操作
Oracle
,只能兼容欧美单字节的文字。
不要依赖平台默认的字符编码方式。例如,中文
Windows
下,默认编码为
GBK
,英文
Linux
下,默认编码为
ISO-8859-1
。依赖平台默认值意味着同样的程序在不同的平台上可能产生不同的结果。
遗留代码攻略
对于第三方的代码,或是以前遗留的代码,如果没有留有指定字符编码的接口,那么这些代码很可能使用默认的系统编码,或是使用固定的字符编码。这样很容易造成上述的各种中文乱码的问题。对于这些代码,我们可以做一个适配器,将它们返回的字符串转换成适当的Unicode
内码。
例如,我们的数据库错误地使用了
ASCII
编码存储文本,也就是说从数据库返回的中文字,实际上被
“
拆
”
成了两个欧洲字符。但是数据库中已经保存了大量数据,想要把它改成正确的
UTF-8
存储格式并不容易。作为权宜之计,我们可以在数据访问层做一个适配器,将欧洲字符重新组合,变成真正的
Unicode
中文。
class
DBAdapter {
private
DBObject obj;
// 重新组合字节,转变成真正的unicode字符串
public
String getString() {
String str = obj.getString();
try
{
return
new
String(str.getBytes(
"8859_1"
),
"GBK"
);
}
catch
(UnsupportedEncodingException e) {
return
str;
// 不会执行到此处
}
}
// 将unicode字符串中的中文拆成两个欧洲字符,以便数据库保存
public
void
setString(String str) {
try
{
str =
new
String(str.getBytes(
"GBK"
),
"8859_1"
);
}
catch
(UnsupportedEncodingException e) {
}
obj.setString(str);
}
}
WEB应用攻略
除了Unicode
以外,无论何种本地字符集,都不能代表所有字符。这将导致一些问题:
难以在一屏幕显示多种语言的文字。
解码用户表单困难。
我们知道浏览器是根据当前页面的
content type
中指定的字符编码来发送用户的表单输入的。假设当前页面的
content type
为
text/html; charset=GBK
,则当用户按下
submit
按钮提交表单时,浏览器自动将用户输入的字符以
GBK
方式编码并发回到服务器端。假设页面的
content type
为
text/html; charset=BIG5
,则用户的输入将以
BIG5
繁体中文的编码发送。但是,如果用户输入的字符超过了这个编码字符集的范围,会怎样呢?我们可以写一个简单的
JSP
做试验:
<
%@page
contentType
=
"text/html; charset=BIG5"
%
>
<
html
>
<
head
>
<
title
>
Form test
</
title
>
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=BIG5"
/>
</
head
>
<
body
>
<
p
>
Character Encoding:
<
%=request.getCharacterEncoding()%
>
</
p
>
<
%
String
mytext
=
request
.getParameter("mytext");
if (mytext != null) {
out.println("
<
p
>
Value of parameter
<
code
>
mytext
<
code
>
:");
out.println("
<
table
border
=
'1'
>
");
out.println("
<
tr
>
<
th
>
Display
</
th
>
<
th
>
Unicode
</
th
>
<
th
>
BIG5 code
</
tr
>
");
for (int
i
=
0
; i
<
mytext.length
(); i++) {
char
ch
=
mytext
.charAt(i);
byte[]
big5bytes
=
Character
.toString(ch).getBytes("BIG5");
int
big5code
=
0
;
out.print("
<
tr
>
<
td
>
" + ch + "
</
td
>
<
td
>
");
out.print(Integer.toHexString(0xFFFF & mytext.charAt(i)) + "
</
td
>
<
td
>
");
for (int
j
=
big5bytes
.length - 1; j
>
= 0; j--) {
big5code
= (big5code
<
<
8
) + (0xFF & big5bytes[j]);
}
out.print(Integer.toHexString(big5code) + "
</
td
>
</
tr
>
");
}
out.println("
</
table
>
</
p
>
");
} else {
mytext
=
""
;
}
%
>
<
form
action
=
"<%=request.getRequestURI()%>"
method
=
"GET"
>
<
textarea
name
=
"mytext"
>
<
%=mytext%
>
</
textarea
>
<
br
>
<
input
type
=
"submit"
/>
</
form
>
</
body
>
</
html
>
上述页面是用
BIG5
显示的。在文本框中打入简体中文字
“
我爱
”
,然后
submit
。在结果页面中,我们可以看到
“
我
”
被转换成了
BIG5
编码
DAA7
,而简体中文
“
爱
”
在
BIG5
中没有对应的编码,因此被浏览器直接以
爱
的形式返回。其中
29233
是简体中文
“
爱
”
的十进制
Unicode
码。
可见浏览器会把超出当前字符集的字符,以实体编码的形式(如
爱
),直接返回给服务器端。
Java servlet
并不会自动处理这样的输入值,这给进一步处理字符串造成了困难。
为什么不直接使用
UTF-8
作为
WEB
页面的编码呢?这样不仅可以让全世界的文字同时显示在同一屏幕上(只要安装了相应的字体),也大大简化了解码用户表单的工作(不需要处理
爱
这样的实体编码)。但使用
UTF-8
也会带来一些微小的不便:
一个中文需要用三个字节表示,稍微增加了网页的大小。但多数网页中的中文字的数量是非常用限的,因而字节数的增加也是非常有限的。例如
Alibaba
中文站的首页面,改成
UTF-8
以后,比
GBK
编码的页面仅仅增加了
1413
个字节。
使用不支持
UTF-8
的编辑器查看页面将看到
“
乱码
”
。但我们可以使用支持
UTF-8
的文本编辑器来查看页面的
HTML
源代码。此外,使用
UTF-8
编码
WEB
页面
并非
意味着用来生成
WEB
页面的模板也必须使用
UTF-8
。仍然可以使用
GBK
来书写
WEB
页面模板。
根据上面的讨论,我们得到如下最佳攻略:
1.
使用
UTF-8
作为
WEB
页面的编码。使用如下语句设置
content type
:
response.setContentType(
"text/html; charset=UTF-8"
);
并且在
WEB
页的
HTML
中设置标记:
<
meta
http-equiv
=
"Content-Type"
content
=
"text/html; charset=UTF-8"
/>
对于
Turbine
,可在其配置文件中设置:
locale.
default
.charset=UTF-
8
Turbine
将根据上述设置,自动为你设置
content type
。
2.
仍然可以使用
GBK
来书写页面模板。对于以
Velocity
为模板系统的
Turbine
,需要在
Turbine
的配置文件中设置:
services.VelocityService.input.encoding=GBK
模板的内容将以
GBK
的方式转换成
Unicode
,最后以
UTF-8
的方式输出到用户浏览器。
3.
使用
UTF-8
解码用户输入的表单。有几种方式可以达到这个目的:
设置服务器特定的配置文件。对于
Resin Server
,需要在其配置文件
resin.conf
中设置:
<web-app character-encoding="UTF-8"/>
;对于
Weblogic Server
,需要设置
WEB-INF/weblogic.xml
配置文件,具体方法参见
BEA文档
。
o
创建一个
javax.servlet.Filter
,在
servlet
被调用前,调用
request.setCharacterEncoding
方法:
import
java.io.IOException;
import
javax.servlet.Filter;
import
javax.servlet.FilterChain;
import
javax.servlet.FilterConfig;
import
javax.servlet.ServletException;
import
javax.servlet.ServletRequest;
import
javax.servlet.ServletResponse;
public
class
SetCharacterEncodingFilter
implements
Filter {
public
void
init(FilterConfig config)
throws
ServletException {
}
public
void
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws
IOException, ServletException {
request.setCharacterEncoding(
"UTF-8"
);
chain.doFilter(request, response);
}
public
void
destroy() {
}
}
o
在
servlet
代码中调用:
"UTF-8"
);
Java Mail
攻略
和
WEB
应用完全类似,使用
Java Mail API
同样需要设置正确的
content type
和字符编码。
import
javax.mail.internet.ContentType;
import
javax.mail.internet.MimeUtility;
import
javax.mail.Part;
...
Part part;
ContentType contentType;
...
contentType =
new
ContentType(
"text/plain"
);
// 或"text/html"
contentType.setParameter(
"charset"
, MimeUtility.mimeCharset(
"UTF-8"
));
part.setContent(
"... text or HTML content ..."
, contentType.toString());
值得注意的是,
RFC 822
标准规定,
的
header
不能包含非
ASCII
编码。也就是说,
的主题(
subject
)不能包含中文。那我们怎样在
的
subject
中发送中文呢?还好,另一个标准
RFC 2047
定义了如何将非
ASCII
的
header
转换成
ASCII
的规则。我们不需要了解这个规则的细节,只要调用
javax.mail.internet.MimeUtility
就可以完成转换了:
import
javax.mail.internet.MimeMessage;
import
javax.mail.internet.MimeUtility;
...
MimeMessage message;
...
message.setSubject(MimeUtility.encodeText(
"... e-mail的主题 ..."
,
MimeUtility.mimeCharset(
"UTF-8"
),
null
));
完成同一个目标,往往有许多种途径,总有一些途径是比较好的,也有一些是不太好的。那些不太好的途径,虽然也能完成任务,却会导致很多潜在的问题。这些问题可能要在一定的条件下才能表现出来。例如,我们公司的
Java
程序以及
Oracle
数据库,从一开始就没有正确使用
Unicode
来存取文本,结果导致了一系列的问题。而要修正这样的问题,代价是比较大的。
怎样的途径是好的呢?一般来说,符合业界标准的,或是市场标准的实现途径,是我们首选的途径。所以我们设计任何程序之前,一定要充分了解相应的标准,不能不求甚解,完成任务了事。
Unicode
标准及相关技术,不仅是解决中文问题的关键,而且是以统一的方法解决国际所有语言文字的问题的最好途径。使用好这些标准,必将为我们公司的产品的进一步向国际化发展,打好坚实的基础。
相关文章推荐
- 中文化和国际化问题权威解析之四:Java中文化和国际化攻略
- 中文化和国际化问题权威解析之四:Java中文化和国际化攻略
- 中文化和国际化问题权威解析之四:Java中文化和国际化攻略
- 中文化和国际化问题权威解析之四:Java中文化和国际化攻略
- 中文化和国际化问题权威解析之二:Java国际化基础
- 中文化和国际化问题权威解析之三:Java中文问题分析
- 中文化和国际化问题权威解析之二:Java国际化基础
- 中文化和国际化问题权威解析之三:Java中文问题分析
- 中文化和国际化问题权威解析之三:Java中文问题分析
- 中文化和国际化问题权威解析之三:Java中文问题分析
- 中文化和国际化问题权威解析之二:Java国际化基础
- 中文化和国际化问题权威解析之三:Java中文问题分析
- 中文化和国际化问题权威解析之二:Java国际化基础
- 中文化和国际化问题权威解析之一:字符编码发展历程
- 中文化和国际化问题权威解析
- 中文化和国际化问题权威解析之六:MIME编码/字符传输编码
- 中文化和国际化问题权威解析之七:JS中的escape、encodeURI、encodeURIComponent解惑
- 中文化和国际化问题权威解析之一:字符编码发展历程(转)
- 中文化和国际化问题权威解析之一:字符编码发展历程
- 中文化和国际化问题权威解析之一:字符编码发展历程