`

中文化和国际化问题权威解析之四:Java中文化和国际化攻略

 
阅读更多

一般攻略

既然在 Java 内部是直接使用 Unicode 表示一切字符的,表达中文自然不成问题。因此所谓的中文问题并不是由 Java 本身引起的。而是因为对 Java Unicode 理解不透或应用不当引起的。下面列出的原则,是解决一切中文问题的总纲:

  1. Java 内部,正确使用 Unicode 标准。对于中文来说,每个汉字使用一个 char 表示。
  2. 在所有的输入输出环节,指明正确的编码方式,进行正确的字符到字节,或字节到字符的转换。
  3. 如果输入源或输出目标直接支持,尽可能直接使用 Unicode 进行输入输出。例如, Oracle 数据库直接支持 UTF-8 的文本数据。使用 UTF-8 操作 Oracle ,可自动兼容所有的语言文字;反之,使用 ISO-8859-1 或者 ASCII 去操作 Oracle ,只能兼容欧美单字节的文字。
  4. 不要依赖平台默认的字符编码方式。例如,中文 Windows 下,默认编码为 GBK ,英文 Linux 下,默认编码为 ISO-8859-1 。依赖平台默认值意味着同样的程序在不同的平台上可能产生不同的结果。

 

遗留代码攻略

对于第三方的代码,或是以前遗留的代码,如果没有留有指定字符编码的接口,那么这些代码很可能使用默认的系统编码,或是使用固定的字符编码。这样很容易造成上述的各种中文乱码的问题。对于这些代码,我们可以做一个适配器,将它们返回的字符串转换成适当的 Unicode 内码。

例如,我们的数据库错误地使用了 ASCII 编码存储文本,也就是说从数据库返回的中文字,实际上被 成了两个欧洲字符。但是数据库中已经保存了大量数据,想要把它改成正确的 UTF-8 存储格式并不容易。作为权宜之计,我们可以在数据访问层做一个适配器,将欧洲字符重新组合,变成真正的 Unicode 中文。

 

  1. public class DBAdapter{
  2. private DBObjectobj;
  3. //重新组合字节,转变成真正的unicode字符串
  4. public StringgetString(){
  5. Stringstr=obj.getString();
  6. try {
  7. return new String(str.getBytes( "8859_1" ), "GBK" );
  8. } catch (UnsupportedEncodingExceptione){
  9. return str; //不会执行到此处
  10. }
  11. }
  12. //将unicode字符串中的中文拆成两个欧洲字符,以便数据库保存
  13. public void setString(Stringstr){
  14. try {
  15. str= new String(str.getBytes( "GBK" ), "8859_1" );
  16. } catch (UnsupportedEncodingExceptione){
  17. }
  18. obj.setString(str);
  19. }
  20. }

WEB应用攻略

除了 Unicode 以外,无论何种本地字符集,都不能代表所有字符。这将导致一些问题:

  1. 难以在一屏幕显示多种语言的文字。
  2. 解码用户表单困难。

我们知道浏览器是根据当前页面的 content type 中指定的字符编码来发送用户的表单输入的。假设当前页面的 content type text/html; charset=GBK ,则当用户按下 submit 按钮提交表单时,浏览器自动将用户输入的字符以 GBK 方式编码并发回到服务器端。假设页面的 content type text/html; charset=BIG5 ,则用户的输入将以 BIG5 繁体中文的编码发送。但是,如果用户输入的字符超过了这个编码字符集的范围,会怎样呢?我们可以写一个简单的 JSP 做试验:

 

  1. < %@page contentType = "text/html;charset=BIG5" % >
  2. < html >
  3. < head >
  4. < title > Formtest </ title >
  5. < meta http-equiv = "Content-Type" content = "text/html;charset=BIG5" />
  6. </ head >
  7. < body >
  8. < p > CharacterEncoding: < %=request.getCharacterEncoding()% > </ p >
  9. < %
  10. String mytext = request .getParameter("mytext");
  11. if(mytext!=null){
  12. out.println(" < p > Valueofparameter < code > mytext < code > :");
  13. out.println(" < table border = '1' > ");
  14. out.println(" < tr > < th > Display </ th > < th > Unicode </ th > < th > BIG5code </ tr > ");
  15. for(int i = 0 ;i < mytext.length ();i++){
  16. char ch = mytext .charAt(i);
  17. byte[] big5bytes = Character .toString(ch).getBytes("BIG5");
  18. int big5code = 0 ;
  19. out.print(" < tr > < td > "+ch+" </ td > < td > ");
  20. out.print(Integer.toHexString(0xFFFF&mytext.charAt(i))+" </ td > < td > ");
  21. for(int j = big5bytes .length-1;j > =0;j--){
  22. big5code =(big5code < < 8 )+(0xFF&big5bytes[j]);
  23. }
  24. out.print(Integer.toHexString(big5code)+" </ td > </ tr > ");
  25. }
  26. out.println(" </ table > </ p > ");
  27. }else{
  28. mytext = "" ;
  29. }
  30. % >
  31. < form action = "<%=request.getRequestURI()%>" method = "GET" >
  32. < textarea name = "mytext" > < %=mytext% > </ textarea > < br >
  33. < input type = "submit" />
  34. </ form >
  35. </ body >
  36. </ html >

上述页面是用

BIG5 显示的。在文本框中打入简体中文字 我爱 ,然后 submit 。在结果页面中,我们可以看到 被转换成了 BIG5 编码 DAA7 ,而简体中文 BIG5 中没有对应的编码,因此被浏览器直接以 &#29233; 的形式返回。其中 29233 是简体中文 的十进制 Unicode 码。

可见浏览器会把超出当前字符集的字符,以实体编码的形式(如 &#29233; ),直接返回给服务器端。 Java servlet 并不会自动处理这样的输入值,这给进一步处理字符串造成了困难。

为什么不直接使用 UTF-8 作为 WEB 页面的编码呢?这样不仅可以让全世界的文字同时显示在同一屏幕上(只要安装了相应的字体),也大大简化了解码用户表单的工作(不需要处理 &#29233; 这样的实体编码)。但使用 UTF-8 也会带来一些微小的不便:

  1. 一个中文需要用三个字节表示,稍微增加了网页的大小。但多数网页中的中文字的数量是非常用限的,因而字节数的增加也是非常有限的。例如 Alibaba 中文站的首页面,改成 UTF-8 以后,比 GBK 编码的页面仅仅增加了 1413 个字节。
  2. 使用不支持 UTF-8 的编辑器查看页面将看到 乱码 。但我们可以使用支持 UTF-8 的文本编辑器来查看页面的 HTML 源代码。此外,使用 UTF-8 编码 WEB 页面 并非 意味着用来生成 WEB 页面的模板也必须使用 UTF-8 。仍然可以使用 GBK 来书写 WEB 页面模板。

根据上面的讨论,我们得到如下最佳攻略:

1. 使用 UTF-8 作为 WEB 页面的编码。使用如下语句设置 content type

  1. response.setContentType( "text/html;charset=UTF-8" );

并且在

WEB 页的 HTML 中设置标记:
  1. < meta http-equiv = "Content-Type" content = "text/html;charset=UTF-8" />

对于

Turbine ,可在其配置文件中设置:
  1. locale. default .charset=UTF- 8

Turbine

将根据上述设置,自动为你设置 content type

2. 仍然可以使用 GBK 来书写页面模板。对于以 Velocity 为模板系统的 Turbine ,需要在 Turbine 的配置文件中设置:

  1. 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 方法:

  1. import java.io.IOException;
  2. import javax.servlet.Filter;
  3. import javax.servlet.FilterChain;
  4. import javax.servlet.FilterConfig;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.ServletRequest;
  7. import javax.servlet.ServletResponse;
  8. public class SetCharacterEncodingFilter implements Filter{
  9. public void init(FilterConfigconfig) throws ServletException{
  10. }
  11. public void doFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)
  12. throws IOException,ServletException{
  13. request.setCharacterEncoding( "UTF-8" );
  14. chain.doFilter(request,response);
  15. }
  16. public void destroy(){
  17. }
  18. }

o

servlet 代码中调用:

  1. request.setCharacterEncoding( "UTF-8" );
Java Mail
攻略

WEB 应用完全类似,使用 Java Mail API 同样需要设置正确的 content type 和字符编码。

  1. import javax.mail.internet.ContentType;
  2. import javax.mail.internet.MimeUtility;
  3. import javax.mail.Part;
  4. ...
  5. Partpart;
  6. ContentTypecontentType;
  7. ...
  8. contentType= new ContentType( "text/plain" ); //或"text/html"
  9. contentType.setParameter( "charset" ,MimeUtility.mimeCharset( "UTF-8" ));
  10. part.setContent( "...textorHTMLcontent..." ,contentType.toString());

值得注意的是,

RFC 822 标准规定, e-mail header 不能包含非 ASCII 编码。也就是说, e-mail 的主题( subject )不能包含中文。那我们怎样在 e-mail subject 中发送中文呢?还好,另一个标准 RFC 2047 定义了如何将非 ASCII header 转换成 ASCII 的规则。我们不需要了解这个规则的细节,只要调用 javax.mail.internet.MimeUtility 就可以完成转换了:
  1. import javax.mail.internet.MimeMessage;
  2. import javax.mail.internet.MimeUtility;
  3. ...
  4. MimeMessagemessage;
  5. ...
  6. message.setSubject(MimeUtility.encodeText( "...e-mail的主题..." ,
  7. MimeUtility.mimeCharset( "UTF-8" ), null ));

大结论

完成同一个目标,往往有许多种途径,总有一些途径是比较好的,也有一些是不太好的。那些不太好的途径,虽然也能完成任务,却会导致很多潜在的问题。这些问题可能要在一定的条件下才能表现出来。例如,我们公司的 Java 程序以及 Oracle 数据库,从一开始就没有正确使用 Unicode 来存取文本,结果导致了一系列的问题。而要修正这样的问题,代价是比较大的。

怎样的途径是好的呢?一般来说,符合业界标准的,或是市场标准的实现途径,是我们首选的途径。所以我们设计任何程序之前,一定要充分了解相应的标准,不能不求甚解,完成任务了事。

Unicode 标准及相关技术,不仅是解决中文问题的关键,而且是以统一的方法解决国际所有语言文字的问题的最好途径。使用好这些标准,必将为我们公司的产品的进一步向国际化发展,打好坚实的基础。

 

分享到:
评论

相关推荐

    java国际化&时间处理

    总的来说,Java的国际化和时间处理涉及多个类和接口,需要熟练掌握`Locale`、`ResourceBundle`、`SimpleDateFormat`(及其替代品`java.time`包)以及`ZoneId`等。理解这些概念并合理运用,可以帮助开发者构建出符合...

    JAVA中文问题解析和最优解决办法

    ### JAVA中文问题解析和最优解决办法 #### 一、引言 随着信息技术的快速发展与全球化进程的加速,软件开发越来越需要支持多语言环境,特别是中文环境。Java作为一门跨平台的编程语言,广泛应用于Web应用、企业级...

    java中文乱码问题详解--- java中文乱码问题详解

    #### 四、解决Java中文问题的方法 针对上述原因,我们可以采取以下措施来解决Java中文乱码问题: 1. **统一编码格式**:确保开发环境中所有相关的编码设置都使用统一的标准,如UTF-8。这包括文件保存时的编码、...

    javaI18N[国际化]

    3. **MessageFormat类**:用于格式化和解析消息,支持参数替换和多种语言的数字、日期和时间格式。它可以与资源包结合使用,动态地根据Locale选择正确的格式。 4. **NumberFormat和DateFormat类**:这两个类分别...

    java实现国际化源码

    在Java开发中,实现国际化(i18n)是一项重要的任务,它允许应用程序根据用户的语言和地区提供相应的本地化内容。本篇文章将详细讲解如何利用Java技术,特别是Struts框架,来实现这一功能。 首先,我们需要了解Java...

    struts中文问题,struts国际化问题——终极解决方案

    ### Struts中文问题与国际化问题——终极解决方案深度解析 #### 引言 在Web应用开发中,特别是使用Struts框架时,中文显示和国际化(Internationalization,简称i18n)问题是开发者常遇的挑战。本文将深入探讨解决...

    protobuf-java-3.11.4-API文档-中文版.zip

    赠送jar包:protobuf-java-3.11.4.jar; 赠送原API文档:protobuf-java-3.11.4-javadoc.jar; 赠送源代码:protobuf-java-3.11.4-sources...人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。

    JAVA_API1.6文档(中文)

    java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...

    java解析文件乱码解决

    本文将深入探讨如何解决Java中文件解析时出现的乱码问题。 首先,理解字符编码至关重要。常见的字符编码有ASCII、ISO-8859-1、GB2312、GBK、GB18030以及Unicode(包括UTF-8、UTF-16等)。在Java中,默认的字符编码...

    JAVA_日期格式化

    在Java编程语言中,日期和时间的...理解并熟练运用这些知识点,你就能在Java项目中自如地处理日期和时间,进行格式化和解析,从而提高代码质量和可维护性。记得在实际编码过程中结合示例实践,这将有助于巩固你的理解。

    专门为java程序员准备的API系列之四:dom4j英文API+中文使用示例

    本系列的第四部分主要针对Java程序员,详细讲解了如何利用DOM4J进行XML的读取、写入、修改以及查询等操作。下面将详细介绍DOM4J的相关知识点。 首先,DOM4J的全称是Document Object Model for Java,它是一个开源...

    国际化插件

    - **MessageFormat**和`ChoiceFormat`:用于格式化和处理动态的多语言文本,如数字、日期或货币格式。 - **Filter和Interceptor**:在MVC框架中,它们可以用于在请求处理之前和之后切换或检测用户所在的区域设置。 ...

    struts1.2 国际化

    `MessageFormat` 类用于格式化和解析带参数的消息字符串,以便根据不同的 `Locale` 显示不同的文本。 - **格式化消息**:使用 `MessageFormat.format(String pattern, Object... arguments)` 方法可以将消息模板中...

    Java类库中文手册

    10. **国际化和本地化**:`java.text`和`java.util.Locale`类支持多语言环境下的软件开发,使程序能适应不同地区的文化和语言习惯。 这份中文手册不仅介绍了上述各个部分的主要类和接口,还可能包括了示例代码、...

    JAVA API官方文档 中文版

    7. **国际化**:`java.text`和`java.util.locale`包提供了支持多种语言和文化的工具。 8. **XML处理**:`javax.xml`包包含了处理XML文档的API,如DOM、SAX和StAX解析器。 9. **Java集合框架**:包括List、Set、Map...

    Java6 API 中文

    7. **国际化与本地化**:`java.text`和`java.util.Locale`支持国际化和本地化功能,允许程序根据用户的地区设置显示相应的内容。 8. **日期与时间**:`java.util.Date`和`java.util.Calendar`是处理日期和时间的...

    定时任务cron 解析为中文.docx

    《定时任务cron解析为中文》 在IT领域,特别是在服务器管理和自动化运维中,定时任务扮演着重要的角色。其中,cron表达式是Unix/Linux系统以及许多其他支持cron的平台用来定义定时任务的一种强大工具。然而,由于其...

    Java实现CSDN文章自动化爬取与解析.zip

    标题中的“Java实现CSDN文章自动化爬取与解析”是指使用Java编程语言编写的一套程序,该程序能够自动地从CSDN(China Software Developer Network)网站上抓取并解析文章内容。CSDN是一个知名的中文IT技术交流平台,...

    java6.0 中文API

    5. **国际化与本地化**:`java.text` 和 `java.util.Locale` 包支持多种语言和文化的本地化处理,包括日期、数字和货币格式的转换,这对于开发全球化软件至关重要。 6. **异常处理**:Java 6.0 引入了一些新的异常...

Global site tag (gtag.js) - Google Analytics