- 浏览: 1595923 次
- 性别:
文章分类
- 全部博客 (2929)
- 非技术 (18)
- Eclipse (11)
- JAVA (31)
- 正则表达式 (0)
- J2EE (4)
- DOS命令 (2)
- WEB前端 (52)
- JavaScript (69)
- 数据库 (8)
- 设计模式 (0)
- JFreechart (1)
- 操作系统 (1)
- 互联网 (10)
- EasyMock (1)
- jQuery (5)
- Struts2 (12)
- Spring (24)
- 浏览器 (16)
- OGNL (1)
- WebService (12)
- OSGi (14)
- 软件 (10)
- Tomcat (2)
- Ext (3)
- SiteMesh (2)
- 开源软件 (2)
- Hibernate (2)
- Quartz (6)
- iBatis (2)
最新评论
我们知道 Unicode 为国际化( I18n )提供了坚实的基础。但是 Unicode 不等同于国际化。使用 Unicode 的 Java 语言,若是使用不当,同样达不到国际化的目的。让我们来看一下 Java 是怎样处理 Unicode 的。
Java 的字符类型
和 C 语言不同, Java 的字符类型 “ char ” 是一个 16 位长的整数,而 C 语言的 char 是 8 位,等同于一个字节,只能表示单字节的字符(拉丁语系文字)。所以 Java 可以直接用一个 char 来表示一个 Unicode 字符(包括中文、英文、日文 …… ),大大简化了字符和字符串的操作。
因为 Java 字符总是 Unicode 字符,所以在后文中,如果不加说明, “ 字符 ” 或 “ char ” 都是指 16 位的 Unicode 字符,而 “ 字节 ” 或 “ byte ” 都是指 8 位字节。
编码( encoding )
然而,当今多数计算机系统,都是以字节为存储运算的基本单元。这就使得在 Java 中,用 Unicode 表示的字符串无法直接写到文件中或保存到数据库中。必须以某一种方式,将字符串转换成便于传输和存储的字节流才行。这种将 Unicode 字符转换成字节的操作,就叫做 “ 字符编码 ” ( encoding )。
前面说过 Unicode 有两种字节表示法: UTF-8 和 UTF-16 。所以将 Unicode 以 UTF-8 和 UTF-16 编码是最直接和自然的事了。以上面的 “ 我爱 Alibaba あいう ” 为例,用 Big-endian (高位字节在前,低位字节在后)的 UTF-16 编码,可以表示成:
我们也可以把同样的字符串转换成 UTF-8 。 UTF-8 是变长的编码,对于 ASCII 码字符,不需要改变,就已经是 UTF-8 了,但一个中文要用三个字节来表示:
使用 UTF-16 或 UTF-8 编码的数据,必须使用支持 Unicode 的软件来处理,例如支持 Unicode 的文本编辑器。目前存在的大量软件,不一定都支持 Unicode 。因此我们往往将 Unicode 转换成某一种本地字符集,例如:
- 英文可转换成 ISO-8859-1 。
- 中文可转换成 GB2312 、 GBK 、 BIG5 或是 GB18030 等。
- 日文可以转换成 SJIS 或 ISO-2022-JP 等。
- 韩文可以转换成 ISO-2022-KR 等。
本地字符集名目之多,无法全部列举。最重要是,大多数字符集只映射到 Unicode 中的部分字符,且字符集之间互相交错,互不兼容。
那么,如果在将 Unicode 转换到某一本地字符集时,发现这一编码字符集不包含这个字符,怎么办呢?例如: “ 我爱 Alibaba” 这个字符串(简体中文),如果转换成繁体中文的 BIG5 编码,就会变成: “ 我 ?Alibaba” 。原来, Unicode 规定,转换时碰到 “ 看不懂 ” 的字符,一律用 “? ( 0x3F ) ” 表示。
这就解释了一种常见的 “ 乱码 ” 情形:好端端的页面,显示在浏览器上却变成了无数个问号。原因就是 Java 在输出网页时,使用了错误的编码方式。后面将更详细地解释这个问题。
解码( decoding )
同样的,如果我们要从文件或数据库中读取文本数据,因为我们读到的是一个字节流,所以我们需要使用正确的编码方法,将字节流恢复成字符流。这个操作叫做 “ 解码 ” ( decoding )。
如果指定了错误的编码方法,那么就会得到不正确的字符流。和编码过程类似, Unicode 规定,在解码时,发现 “ 看不懂 ” 的字节,一律用 “ � ( 0xFFFD ) ” 表示。例如:将 “ 我爱 Alibaba” 以 UTF-8 的编码方式保存在一个文件中,用繁体中文编码 BIG5 读入,就会变成: “ ����� 婢 libaba” 。因为 UTF-8 字节序列 E6 88 91 E7 88 不是一个合法的 BIG5 编码序列,而第六个字节 B1 和后面一个字节 41 (原本是字母 “A” )碰巧可以构成一个 BIG5 字符 “ 婢 ” 。
反过来说,是不是经过解码的字符序列中,不包含问号 “?” ,就代表解码方法是正确的呢?显然不是!
最典型的错误就是:用 ISO-8859-1 来解码中文文件。这导致了更隐蔽的错误。因为 ISO-8859-1 的字符编码正好和 Unicode 的最前面 256 个字符相同,换句话说,在 ISO-8859-1 编码之前加上 “00” 就变成了 Unicode 。正是由于这个特殊性, ISO-8859-1 似乎成了 “ 万能 ” 的编码而被广泛地误用!
仍以 “ 我爱 Alibaba” 为例,如果用 ISO-8859-1 解码此文件,我们可以得到一个看似 “ 合法 ” 的字符串:
很明显,使用 ISO-8859-1 解码中文文件的人,只是把 Unicode 字符看作是 16 位的 “ 字节 ” 而已。对 Java 而言, “ 我爱 ” 这两个字符不代表中文字符 “ 我爱 ” ,只不过是 4 个欧洲字符和符号而已。
Java 对国际化的支持
Java I/O
在 Java 中,主要是通过输入输出流来进行编码和解码的。输入输出流分成两种:
字节流( Octet Stream )
从 java.io.InputStream 或 java.io.OutputStream 派生的,负责读写字节( byte )。 例如: java.io.FileInputStream 、 java.io.ByteArrayInputStream 、 java.io.FileOutputStream 、 java.io.ByteArrayOutputStream 等。
字符流( Character Stream )
从 java.io.Reader 或 java.io.Writer 派生的,负责读写字符( char )。例如: java.io.StringReader 、 java.io.StringWriter 等。
而联系这两种流的,分别是 OutputStreamWriter 和 InputStreamReader 。由这两个类来实现 Java 字符的编码和解码。
下面的完整的例子演示了 Java 如何把一个包含中文的字符串,以 GBK 编码的方式保存到一个文本文件中。
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.OutputStreamWriter;
- public class TestEncoding{
- public static void main(String[]args){
- try {
- writeStringToFile( "我爱Alibaba" , "c:/ilovealibaba.txt" , "GBK" );
- } catch (IOExceptione){
- e.printStackTrace();
- }
- }
- public static void writeStringToFile(Stringstr,Stringfilename,
- Stringcharset) throws IOException{
- FileOutputStreamostream= new FileOutputStream(filename);
- OutputStreamWriterwriter= new OutputStreamWriter(ostream,charset);
- try {
- writer.write(str);
- } finally {
- writer.close();
- }
- }
- }
当然,除了输出到文件,事实上可以使用任何输出流,例如使用
ByteArrayOutputStream 将可将字节流保存在内存中。
下面的完整的例子演示了 Java 如何读取一个文件,并把文件的内容以 GBK 方式解码。
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStreamReader;
- public class TestDecoding{
- public static void main(String[]args){
- try {
- System.out
- .println(readStringFromFile( "c:/ilovealibaba.txt" , "GBK" ));
- } catch (IOExceptione){
- e.printStackTrace();
- }
- }
- public static StringreadStringFromFile(Stringfilename,Stringcharset)
- throws IOException{
- FileInputStreamistream= new FileInputStream(filename);
- InputStreamReaderreader= new InputStreamReader(istream,charset);
- StringBufferstring= new StringBuffer();
- char []buffer= new char [ 128 ];
- int count= 0 ;
- try {
- while ((count=reader.read(buffer))!=- 1 ){
- string.append(buffer, 0 ,count);
- }
- } finally {
- reader.close();
- }
- return string.toString();
- }
- }
当然也可以从任何输入流中获得字节,然后用同样的方法转换成字符。例如,通过
ByteArrayInputStream ,可以从内存中的 byte[] 数组中取得字节流。
字符串处理
另一种常见的编码和解码的方法,是通过 Java 的 String 类完成的。下面的程序演示了 Java 如何使用 String.getBytes() 方法,将字符串编码成指定形式的字节的。
- import java.io.UnsupportedEncodingException;
- public class TestStringGetBytes{
- public static void main(String[]args){
- try {
- dumpBytes( "我爱Alibaba" , "GBK" );
- } catch (UnsupportedEncodingExceptione){
- e.printStackTrace();
- }
- }
- public static void dumpBytes(Stringstr,Stringcharset)
- throws UnsupportedEncodingException{
- byte []bytes=str.getBytes(charset);
- //显示bytes的内容,每行显示4个
- for ( int i= 0 ;i<bytes.length;i++){
- System.out.print(Integer.toHexString(bytes[i]& 0xFF ));
- System.out.print( "" );
- if ((i+ 1 )% 4 == 0 ){
- System.out.println();
- }
- }
- System.out.println();
- }
- }
运行的结果为:
下面的程序,使用 注意:
其它和国际化相关的功能
java.util.ResourceBundle
通过 ResourceBundle ,我们可以把特定语言相关的信息放在程序之外。这样当我们要在已有产品的基础上,增加一种语言或地区的支持时,只需要增加一种 ResourceBundle 的实现即可。
数字、货币、日期、时间的格式化
中国人表示日期的习惯是: “2003 年 5 月 24 日 星期六 ” ,而美国人则习惯于: “Saturday, May 24, 2003” 。 Java 程序代码可以不关心这些差别。在运行时刻, Java 可以根据不同的语言或地区习惯,自动按不同的格式风格显示这些内容。
- import java.text.DateFormat;
- import java.util.Date;
- import java.util.Locale;
- public class TestDateFormat{
- public static void main(String[]args){
- Datedate= new Date();
- System.out.println(DateFormat.getDateInstance(DateFormat.FULL,Locale.CHINA).format(date));
- System.out.println(DateFormat.getDateInstance(DateFormat.FULL,Locale.US).format(date));
- }
- }
除了
DateFormat , java.text 包中还包括了很多其它格式化类。
1. NumberFormat
2. DecimalFormat
3. DateFormat
4. SimpleDateFormat
5. MessageFormat
6. ChoiceFormat
检测字符属性
前文提到, Unicode 不仅定义了统一的字符集,而且为这些字符及编码数据提出应用的方法以及对语义数据进行补充。而 Java 可以直接查看 Unicode 所定义的这些字符属性。
传统的非国际化的程序常常这样检测一个字符是否属于字母、数字还是空白:
- char ch;
- ...
- if ((ch>= 'a' &&ch<= 'z' )||(ch>= 'A' &&ch<= 'Z' )){
- //ch是一个字母
- }
- ...
- if (ch>= '0' &&ch<= '9' ){
- //ch是一个数字
- }
- ...
- if ((ch== '' )||(ch== '/r' )||(ch== '/n' )||(ch== '/t' )){
- //ch是一个空白
- }
这样的程序没有考虑除了英文和其它少数几种语言之外的语言习惯。例如:西腊字母
“αβγ”
也应该算是字母,汉字中全角数字
“
123
”
也是数字,全角空格
“
”
(
U+3000
)也属于空白。正确的程序应该是这样的:
下面列出了
1. Character.isDigit
2. Character.isLetter
3. Character.isLetterOrDigit
4. Character.isLowerCase
5. Character.isUpperCase
6. Character.isSpaceChar
7. Character.isDefined
此外, Unicode 还为每个统一字符定义了很多属性。我们可以通过 Character 相应方法取得这些属性。例如可以用下面的代码判定一个字符是否为 “ 中日韩统一汉字 ” :
- char ch;
- ...
- Character.UnicodeBlockblock=Character.UnicodeBlock.of(ch);
- if (block==Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS){
- //是CJK统一汉字
- }
更多
Character 类细节请参阅 Java API文档 。
字符串比较和排序
字符间的逻辑顺序不一定和 Unicode 编码的数值顺序一致。利用 java.text.Collator 可以比较两个 Unicode 字符串的逻辑顺序。
检测字符串的边界
在应用中,我们经常需要检测字符串的边界:检测字符( character )、词( word )、句子( sentence )、行( line )的边界。例如,显示一段文字,需要在屏幕的右边界处对文本断行。断行不是任意的。例如,你不能把一个英文单词拆开。
使用 java.text.BreakIterator 可以实现字符串边界的检测。
以上只是简单地列举了 Java 中和国际化相关的功能。具体描述这些内容,超出了本文的议题。可以从 Java 文档中取得更详细的信息: Java国际化指南 。
发表评论
-
中文化和国际化问题权威解析之一:字符编码发展历程
2009-08-11 20:06 776中文化和国际化问题权威解析之一:字 ... -
中文化和国际化问题权威解析之三:Java中文问题分析
2009-08-11 20:08 665本章从实际的中文问题中,分析问题的根本原因,以及解决之道。 ... -
中文化和国际化问题权威解析之五:URL编码/Misc
2009-08-11 20:09 592通过前面中文化、国际化问题解决的系列1-4,相信大家对字符集、 ... -
中文化和国际化问题权威解析之四:Java中文化和国际化攻略
2009-08-11 20:09 636一般攻略 既然在 Java 内部是直接使用 U ... -
中文化和国际化问题权威解析之七:JS中的escape、encodeURI、encodeURIComponent解惑
2009-08-11 20:10 611面一篇文档《中文化和国际化问题权威解析之五:URL编码/Mis ... -
中文化和国际化问题权威解析之六:MIME编码/字符传输编码
2009-08-11 20:10 600MIME(Multipurpose Internet Ma ... -
通过IP地址和子网掩码与运算计算相关地址
2010-03-12 14:41 480通过IP地址和子网掩码 ... -
子网掩码和IP地址计算网络地址和广播地址的换算
2010-03-12 14:50 1114子网掩码和IP地址计算 ... -
更改google桌面搜索的索引文件位置
2010-11-15 09:14 648Google Desktop Search默认的索引文件是保存 ...
相关推荐
java实现国际化中英文语言切换 java语言切换JSP国际化 java实现国际化中英文语言切换 java语言切换JSP国际化
赠送jar包:scala-java8-compat_2.11-0.7.0.jar; 赠送原API文档:scala-java8-compat_2.11-0.7.0-javadoc.jar; 赠送源代码:scala-java...人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。
java国际化中文乱码问题解决包ResourceBundleEditor_v0.8.0.zip
赠送jar包:aliyun-java-sdk-kms-2.11.0.jar; 赠送原API文档:aliyun-java-sdk-kms-2.11.0-javadoc.jar...人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心使用。 双语对照,边学技术、边学英语。
jar包里面包含了:hapi解析HL7的代码范例,所需jar包。亲测可用。《Java版本》
java.util 包含 collection 框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组)。 java.util.concurrent 在并发编程中很常用的实用工具类...
JAVA2核心技术第1卷:基础知识(原书第7版)(PDF中文版)part1(压缩包名:JAVA2核心技术第1卷:基础知识(原书第7版).part1); JAVA2核心技术第1卷:基础知识(原书第7版)(PDF中文版)part2(压缩包名:JAVA2核心技术第1...
java国际化实例,内置源码,还有说明文档。源码包里有编译好的jar文件,已经可以执行,可以实现中文和英文界面的切换,无需要安装插件,调用系统资源,有需要的朋友们请下载吧。 个人体会:做项目时需要用到国际化的...
中文乱码问题与国际化,如何避免中文乱码问题!
java中文乱码问题详解--- java中文乱码问题详
第1章 :简单地介绍了Java虚拟机的历史并... 第5章:定义了Java虚拟机启动以及类和接口的加载、链接和初始化的过程; 第6章:定义了Java虚拟机指令集; 第7章:提供了一张以操作码值为索引的Java虚拟机操作码助记表。
java 解析csv文件例子,csv文件 中文乱码问题
赠送jar包:javacv-1.5.7.jar; 赠送原API文档:javacv-1.5.7-javadoc.jar; 赠送源代码:javacv-1.5.7-sources.jar; 赠送Maven依赖信息文件:javacv-1.5.7.pom; 包含翻译后的API文档:javacv-1.5.7-javadoc-API...
经典的struts中文问题,国际化问题终极解决方案
中文名: 编程实践:Java进阶100例 原名: 编程实践:Java进阶100例 别名: Java 作者: 李相国等. 译者: 李相国等. 图书分类: 软件 资源格式: PDF 版本: 影印版 出版社: 李相国等. 书号: 9787111372370. 发行...
java解析DXF文件信息,这里涉及到各种转换字符串编码的操作,解决了解析过程中遇到中文的乱码问题,包括circle,ellipse,line,lwpolyline等,还涉及到从DXF文件中读取一个字符串、判断CIRCLE实体、判断ellipse实体、...
资源名称:Pro JPA2中文版:精通Java持久化API内容简介:本书上一版的目标之一是尽可能地简单明了,但是不短缺。我们尽力调整了它的大小,从而只描述我们感觉值得讲述的内容,但是依然确保适合在您的行李中携带本书...
解析Java中文乱码的处理方法解析Java中文乱码的处理方法解析Java中文乱码的处理方法解析Java中文乱码的处理方法解析Java中文乱码的处理方法
23种设计模式 GoF设计模式 SQL Server死锁 UML2.0建模 JavaUtil包使用 Java中文问题全面解析 SWT JFace全面接触 Eclipse插件开发 Java5年经典文章 Java核心技术之线程 软件工程 人月神话 Java模板引擎