您的位置:首页 > 编程语言 > Java开发

中文化和国际化问题权威解析之四: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

中文。


public
 
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文档





创建一个
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() {

    }

}




servlet
代码中调用:



request.setCharacterEncoding(
"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

标准规定,
e-mail

header
不能包含非
ASCII

编码。也就是说,
e-mail
的主题(
subject
)不能包含中文。那我们怎样在
e-mail

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

标准及相关技术,不仅是解决中文问题的关键,而且是以统一的方法解决国际所有语言文字的问题的最好途径。使用好这些标准,必将为我们公司的产品的进一步向国际化发展,打好坚实的基础。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息