中文化和国际化问题权威解析之二:Java国际化基础
2010-10-27 11:20
225 查看
我们知道
Unicode
为国际化(
I18n
)提供了坚实的基础。但是
Unicode
不等同于国际化。使用
Unicode
的
Java
语言,若是使用不当,同样达不到国际化的目的。让我们来看一下
Java
是怎样处理
Unicode
的。
和
C
语言不同,
Java
的字符类型
“
char
”
是一个
16
位长的整数,而
C
语言的
char
是
8
位,等同于一个字节,只能表示单字节的字符(拉丁语系文字)。所以
Java
可以直接用一个
char
来表示一个
Unicode
字符(包括中文、英文、日文
……
),大大简化了字符和字符串的操作。
因为
Java
字符总是
Unicode
字符,所以在后文中,如果不加说明,
“
字符
”
或
“
char
”
都是指
16
位的
Unicode
字符,而
“
字节
”
或
“
byte
”
都是指
8
位字节。
然而,当今多数计算机系统,都是以字节为存储运算的基本单元。这就使得在
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
)。
如果指定了错误的编码方法,那么就会得到不正确的字符流。和编码过程类似,
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 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
(IOException e) {
e.printStackTrace();
}
}
public
static
void
writeStringToFile(String str, String filename,
String charset)
throws
IOException {
FileOutputStream ostream =
new
FileOutputStream(filename);
OutputStreamWriter writer =
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
(IOException e) {
e.printStackTrace();
}
}
public
static
String readStringFromFile(String filename, String charset)
throws
IOException {
FileInputStream istream =
new
FileInputStream(filename);
InputStreamReader reader =
new
InputStreamReader(istream, charset);
StringBuffer string =
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
(UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public
static
void
dumpBytes(String str, String charset)
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();
}
}
运行的结果为:
ce d2 b0 ae
41
6c
69
62
61
62
61
下面的程序,使用
String(bytes, charset)
构造函数,也实现了读取一个文件的内容,并以指定编码方式(
GBK
)解码成字符串的功能。
import
java.io.ByteArrayOutputStream;
import
java.io.FileInputStream;
import
java.io.IOException;
public
class
TestNewString {
public
static
void
main(String[] args) {
try
{
System.out.println(
readStringFromFile(
"c:/ilovealibaba.txt"
,
"GBK"
));
}
catch
(IOException e) {
e.printStackTrace();
}
}
public
static
String readStringFromFile(String filename, String charset)
throws
IOException {
FileInputStream istream =
new
FileInputStream(filename);
ByteArrayOutputStream ostream =
new
ByteArrayOutputStream();
byte
[] buffer =
new
byte
[
128
];
int
count =
0
;
try
{
while
((count = istream.read(buffer)) != -
1
) {
ostream.write(buffer,
0
, count);
}
}
finally
{
istream.close();
}
byte
[] stringBytes = ostream.toByteArray();
// 使用指定charset,将bytes[]转换成字符串
return
new
String(stringBytes, charset);
}
}
注意:
上面这段程序只是演示
String(bytes, charset)
构造函数,如果要读取大量的文本,这种方式的性能肯定不如前面使用
InputStreamReader
的程序示例。
其它和国际化相关的功能
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) {
Date date =
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
)也属于空白。正确的程序应该是这样的:
char
ch;
...
if
(Character.isLetter(ch)) {
...
if
(Character.isDigit(ch)) {
...
if
(Character.isSpaceChar(ch)) {
...
下面列出了
Character
中用来判定字符属性的方法:
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.UnicodeBlock block = 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国际化指南
。
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
(IOException e) {
e.printStackTrace();
}
}
public
static
void
writeStringToFile(String str, String filename,
String charset)
throws
IOException {
FileOutputStream ostream =
new
FileOutputStream(filename);
OutputStreamWriter writer =
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
(IOException e) {
e.printStackTrace();
}
}
public
static
String readStringFromFile(String filename, String charset)
throws
IOException {
FileInputStream istream =
new
FileInputStream(filename);
InputStreamReader reader =
new
InputStreamReader(istream, charset);
StringBuffer string =
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
(UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public
static
void
dumpBytes(String str, String charset)
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();
}
}
运行的结果为:
ce d2 b0 ae
41
6c
69
62
61
62
61
下面的程序,使用
String(bytes, charset)
构造函数,也实现了读取一个文件的内容,并以指定编码方式(
GBK
)解码成字符串的功能。
import
java.io.ByteArrayOutputStream;
import
java.io.FileInputStream;
import
java.io.IOException;
public
class
TestNewString {
public
static
void
main(String[] args) {
try
{
System.out.println(
readStringFromFile(
"c:/ilovealibaba.txt"
,
"GBK"
));
}
catch
(IOException e) {
e.printStackTrace();
}
}
public
static
String readStringFromFile(String filename, String charset)
throws
IOException {
FileInputStream istream =
new
FileInputStream(filename);
ByteArrayOutputStream ostream =
new
ByteArrayOutputStream();
byte
[] buffer =
new
byte
[
128
];
int
count =
0
;
try
{
while
((count = istream.read(buffer)) != -
1
) {
ostream.write(buffer,
0
, count);
}
}
finally
{
istream.close();
}
byte
[] stringBytes = ostream.toByteArray();
// 使用指定charset,将bytes[]转换成字符串
return
new
String(stringBytes, charset);
}
}
注意:
上面这段程序只是演示
String(bytes, charset)
构造函数,如果要读取大量的文本,这种方式的性能肯定不如前面使用
InputStreamReader
的程序示例。
其它和国际化相关的功能
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) {
Date date =
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
)也属于空白。正确的程序应该是这样的:
char
ch;
...
if
(Character.isLetter(ch)) {
...
if
(Character.isDigit(ch)) {
...
if
(Character.isSpaceChar(ch)) {
...
下面列出了
Character
中用来判定字符属性的方法:
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.UnicodeBlock block = 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国际化指南
。
相关文章推荐
- 中文化和国际化问题权威解析之二:Java国际化基础
- 中文化和国际化问题权威解析之二:Java国际化基础
- 中文化和国际化问题权威解析之二:Java国际化基础
- 中文化和国际化问题权威解析之三:Java中文问题分析
- 中文化和国际化问题权威解析之三:Java中文问题分析
- 中文化和国际化问题权威解析之四:Java中文化和国际化攻略
- 中文化和国际化问题权威解析之二:Java国际化基础
- 中文化和国际化问题权威解析之三:Java中文问题分析
- 中文化和国际化问题权威解析之四:Java中文化和国际化攻略
- 中文化和国际化问题权威解析之四:Java中文化和国际化攻略
- 中文化和国际化问题权威解析之三:Java中文问题分析
- 中文化和国际化问题权威解析之三:Java中文问题分析
- 中文化和国际化问题权威解析之四:Java中文化和国际化攻略
- 中文化和国际化问题权威解析之四:Java中文化和国际化攻略
- 中文化和国际化问题权威解析之七:JS中的escape、encodeURI、encodeURIComponent解惑
- 中文化和国际化问题权威解析
- 中文化和国际化问题权威解析之一:字符编码发展历程
- 中文化和国际化问题权威解析之一:字符编码发展历程(转)
- 中文化和国际化问题权威解析之一:字符编码发展历程
- 中文化和国际化问题权威解析之一:字符编码发展历程