`
jaychang
  • 浏览: 728086 次
  • 性别: Icon_minigender_1
  • 来自: 嘉兴
社区版块
存档分类
最新评论

Java字符串与字符集的基本概念

 
阅读更多

Java String是Java API中最常用的类,本文和大家谈谈String类的内部原理,同时描述ISO-8859-1字符集在字符串处理中的独特用处。 

Java字符串的内部编码

String类内部管理着一个char类型的数组,Java API是这样描述char基本类型的:
char 数据类型(和 Character 对象封装的值)基于原始的 Unicode 规范,将字符定义为固定宽度的 16 位实体。
这一点我们可以通过下面的语句加以证实:

 

[java] view plaincopy
  1. System.out.println(Character.SIZE); // 结果为16  

 

 

根据String类的构造方法,我们可以这样定义字符串:

 

[java] view plaincopy
  1. String str = "abc";  

 

 

上句代码等效于:

 

[java] view plaincopy
  1. char data[] = {'a''b''c'};  
  2. String str = new String(data);  

 

 

同时,char基本类型与byte、short、int、long一样可以用数值表示。所以上述代码等效于:

 

[java] view plaincopy
  1. char data[] = {0x610x620x63};  
  2. String str = new String(data);  

 

 

需要注意的是,char基本类型始终使用16位即两个字节表示一个字符,即使这个字符的Unicode值小于0xFF(如ASCII码)。对于Unicode值大于0xFF的字符,如“中国”二字的Unicode编码分别为/u4E2D和/u56FD,我们可以这样创建:

 

[java] view plaincopy
  1. char data[] = {'/u4E2D''/u56FD'};  
  2. String str = new String(data);  

 

 

当然也可以这样创建:

 

[java] view plaincopy
  1. char data[] = {0x4E2D0x56FD};  
  2. String str = new String(data);  

 

 

通过上面的描述可以明确两个要点:

字符串对象中的每一个元素始终占据两个字节长度,一个或两个元素(增补字符占据两个元素)表示一个字符。
字符串对象中的每一个元素都使用Unicode字符集进行编码。

字符集(Charset)与字节化字符数据

在出现 Unicode 规范之前,计算机在处理字符串的问题上经历过ASCII和ANSI编码两个阶段,在ASCII时代,计算机只能处理英文数字以及几个基本符号,当时使用的是单字节字符集(SBCS)
各国为了能在计算机上处理本国的文字,制订了相应的国家标准,规定了各自的ANSI编码。如中文简体使用GBK标准;中文繁体使用BIG5标准;日文使用Shift_JIS标准。在ANSI编码时代,计算机使用多字节字符集(MBCS)处理文字。如“中国ABC”,在GB2312标准中,“中国”两个字符分别使用两个字节表示,而“ABC”三个英文字符又分别使用一个字节表示。各国文字的ANSI编码互不通用,不能使用一种ANSI编码表达多个国家的文字。
为了文字交流的顺畅,也就是说为了达到在一个文本当中既可以有中文简体字存在,也可以有中文繁体字存在的目的。国际组织根据各国语言的特点,使用两个字节的数据量将大部分国家的文字信息整合到一个字符集中,这就是Unicode编码,也称万国码。关于Unicode编码的特点,前文已经描述,那就是使用双字节字符集(DBCS)处理文字。


在Java中,使用字节数组保存不同字符集的字符值,如使用ASCII字符集保存“abc”的方法如下:

 

[java] view plaincopy
  1. // 使用ASCII编码的“abc”  
  2. byte[] ascBytes = {(byte)0x61, (byte)0x62, (byte)0x63};  

 

 

使用GBK字符集保存“中国”二字的方法如下:

 

[java] view plaincopy
  1. // 使用GBK编码的“中国”  
  2. byte[] gbkBytes = {(byte)0xD6, (byte)0xD0, (byte)0xB9, (byte)0xFA};  

 

 

当然我们也可以使用Unicode字符集保存“中国”二字。如:

 

[java] view plaincopy
  1. // 使用Unicode编码的“中国”  
  2. byte[] unicodeBytes = {(byte)0x4E, (byte)0x2D, (byte)0x56, (byte)0xFD};  

 

 

一个特殊的字符集UTF-8是与Unicode规范对应的多字节表示的字符集。如“中”字的UTF-8编码为“0xE4, 0xB8, 0xAD”三个字节。

 

在这里,将这些与具体字符集相对应的字节化的数据流称为字节化字符数据,与字符串对象形成鲜明对照的是,字符串对象的最小单位是两个字节而字节化字符数据的最小单位则是一个字节。由此我们可以明确另外两个要点:

字节化字符数据中的每一个元素始终占据一个字节长度,一个或多个元素表示一个字符。
字节化字符数据必须与一个字符集相对应。

字节化字符数据与字符串对象的互换

在Java程序运行过程中,字符串对象始终以Unicode编码方式保存在内存中,但将字符串对象保存到持久化资源(文件或数据库)或将其通过网络传输时,通常是以字节化字符数据的方式进行处理。这样就要求Java API必须提供两者互换的功能。事实上这一功能在String类及Charset类中已经提供。
一方面我们可以利用String类的getBytes()方法返回不同字符集的字节化字符数据,其本质是从Unicode字符集编码向其它字符集编码转换的过程。例如:

 

[java] view plaincopy
  1. public static void main(String[] args) {  
  2.   
  3.     String str = "中国";  
  4.     printBytes("中国的UNICODE编码:", str.getBytes(Charset.forName("unicode")));  
  5.     printBytes("中国的GBK编码:", str.getBytes(Charset.forName("GBK")));  
  6.     printBytes("中国的UTF-8编码:", str.getBytes(Charset.forName("UTF-8")));  
  7. }  
  8.   
  9. public static void printBytes(String title, byte[] data) {  
  10.     System.out.println(title);  
  11.     for (byte b : data) {  
  12.         System.out.print("0x" + toHexString(b) + " ");  
  13.     }  
  14.     System.out.println();     
  15. }  
  16.   
  17. public static String toHexString(byte value) {  
  18.     String tmp = Integer.toHexString(value & 0xFF);  
  19.     if (tmp.length() == 1) {  
  20.         tmp = "0" + tmp;  
  21.     }  
  22.   
  23.     return tmp.toUpperCase();  
  24. }  

 

 

上例的输出结果为:

中国的UNICODE编码:
0xFE 0xFF 0x4E 0x2D 0x56 0xFD 
中国的GBK编码:
0xD6 0xD0 0xB9 0xFA 
中国的UTF-8编码:
0xE4 0xB8 0xAD 0xE5 0x9B 0xBD

需要注意的是,从字符串对象中取出的Unicode编码的字节化字符数据时,其开始部分存在一个BOM(ByteOrderMark),一般情况下,该BOM值为“0xFE 0xFF”,即大端字节序(BIG_ENDIAN)。如果BOM值为“0xFF 0xFE”则为小端字节序(LITTLE_ENDIAN)。

 

另一方面也可以利用String类的构造方法根据不同字符集的字节化字符数据产生一个字符串对象,其本质是从其它字符集编码向Unicode字符集编码转换的过程。例如:

 

[java] view plaincopy
  1. byte[] unicodeBytes = {(byte)0x4E, (byte)0x2D, (byte)0x56, (byte)0xFD};  
  2. System.out.println(new String(unicodeBytes, Charset.forName("unicode")));  
  3.   
  4. byte[] gbkBytes = {(byte)0xD6, (byte)0xD0, (byte)0xB9, (byte)0xFA};  
  5. System.out.println(new String(gbkBytes, Charset.forName("GBK")));  
  6.           
  7. byte[] utf8Bytes = {(byte)0xE4, (byte)0xB8, (byte)0xAD, (byte)0xE5, (byte)0x9B, (byte)0xBD};  
  8. System.out.println(new String(utf8Bytes, Charset.forName("UTF-8")));  

 

 

上例三个输出语句均输出“中国”二字。

上述两种转换过程,特别是Unicode字符集编码向其它字符集编码的转换过程中会出现转换失败的现象。转换失败时该Unicode码自动用0x3F代替。例如:

 

[java] view plaincopy
  1. public static void main(String[] args) {  
  2.   
  3.     String str = "中国";  
  4.     printBytes("中国的BIG5编码:", str.getBytes(Charset.forName("BIG5")));  
  5. }  
  6.   
  7. public static void printBytes(String title, byte[] data) {  
  8.     // 同上例  
  9. }  

 

 

上例的输出结果为:

中国的BIG5编码:
0xA4 0xA4 0x3F

 

其中“国”由于没有繁体中文BIG5字符集对应的编码值,所以会用0x3F表示。

特殊的字符集(ISO-8859-1)

ISO-8859-1是单字节字符集,是ASCII字符集的补充。通常情况下使用ISO-8859-1字符集进行字符串对象与字节化字符数据的互换操作与前述完全一致。例如:

 

[java] view plaincopy
  1. public static void main(String[] args) {  
  2.     // 字符串“abc”  
  3.     byte[] bytes = { (byte0x61, (byte0x62, (byte0x63 };  
  4.     String str = new String(bytes, Charset.forName("ISO-8859-1"));  
  5.     printBytes("ISO-8859-1编码:", str.getBytes(Charset.forName("ISO-8859-1")));  
  6.     printBytes("UNICODE编码:", str.getBytes(Charset.forName("UNICODE")));          
  7. }  
  8.   
  9. public static void printBytes(String title, byte[] data) {  
  10.     // 同上  
  11. }  

 


上例的输出结果为:

ISO-8859-1编码:
0x61 0x62 0x63 
UNICODE编码:
0xFE 0xFF 0x00 0x61 0x00 0x62 0x00 0x63

通过此例可以看出,从ISO-8859-1字符集转换成Unicode字符集的过程是将字节化字符数据中的每个一个byte类型元素直接保存成一个char类型元素。也就是说下面的代码:

 

[java] view plaincopy
  1. byte[] bytes = { (byte0x61, (byte0x62, (byte0x63 };  
  2. String str = new String(bytes, Charset.forName("ISO-8859-1"));  

 

 

等效于:

 

[java] view plaincopy
  1. byte[] bytes = { (byte0x61, (byte0x62, (byte0x63 };  
  2. char[] tmp = new char[bytes.length];  
  3. for (int i=0; i<bytes.length; i++) {  
  4.     tmp[i] = (char)bytes[i];  
  5. }  
  6. String str = new String(tmp);  

 

 

需要注意的是,ISO-8859-1到Unicode的转换过程是对编码值为0x00 - 0xFF之间都有效的一种转换。在ISO-8859-1字符集中,0x00-0x1F、0x7F、0x80-0x9F没有定义。我们可以使用其中几个无效编码进行测试:

 

[java] view plaincopy
  1. public static void main(String[] args) {  
  2.     // 无效的ISO-8859-1编码  
  3.     byte[] bytes = { (byte0x00, (byte0x1A, (byte0x7F, (byte0x93 };  
  4.     String str = new String(bytes, Charset.forName("ISO-8859-1"));  
  5.     printBytes("ISO-8859-1编码:", str.getBytes(Charset.forName("ISO-8859-1")));  
  6.     printBytes("UNICODE编码:", str.getBytes(Charset.forName("UNICODE")));          
  7. }  
  8.   
  9. public static void printBytes(String title, byte[] data) {  
  10.     // 同上  
  11. }  

 

 

上例的输出结果为:

ISO-8859-1编码:
0x00 0x1A 0x7F 0x93 
UNICODE编码:
0xFE 0xFF 0x00 0x00 0x00 0x1A 0x00 0x7F 0x00 0x93

根据这一特点,我们可以总结出最后一个要点:

利用ISO-8859-1字符集,我们可以将任何一个字节数组无损保存到字符串对象中。

也就是说,可以利用这一特点将字节化字符数据的原始字节数据(而不是经过Unicode字符集转换之后的数据)直接保存在字符串对象中。反之也可以从一个经过ISO-8859-1编码的字符串对象中取出原始字节数据。例如:

 

[java] view plaincopy
  1. public static void main(String[] args) {  
  2.     byte[] utf8Bytes = {(byte)0xE4, (byte)0xB8, (byte)0xAD, (byte)0xE5, (byte)0x9B, (byte)0xBD};  
  3.     printBytes("原始字节流:" , utf8Bytes);  
  4.       
  5.     // ISO-8859-1编码过程  
  6.     // 保存原始字节数据流(不经过Unicode编码)到字符串对象  
  7.     String isoStr = new String(utf8Bytes, Charset.forName("ISO-8859-1"));  
  8.       
  9.     // ISO-8859-1解码过程  
  10.     // 从字符串对象中取得与utf8Bytes内容完全相等的原始字节数据流  
  11.     byte[] tmp = isoStr.getBytes(Charset.forName("ISO-8859-1"));  
  12.     printBytes("转换字节流:" , tmp);  
  13. }  
  14.   
  15. public static void printBytes(String title, byte[] data) {  
  16.     // 同上  
  17. }  

 


上例的输出结果为:

原始字节流:
0xE4 0xB8 0xAD 0xE5 0x9B 0xBD 
转换字节流:
0xE4 0xB8 0xAD 0xE5 0x9B 0xBD

这种通过字符串对象保存原始字节数据的方法被很多地方所使用。最常见的就是Java WEB应用中Web服务器对来自于服务器的表单数据的处理,关于这方面的详细说明请参考 如何解决Java WEB应用中的乱码问题

分享到:
评论

相关推荐

    java 字符串转16进制Ascii

    在Java编程语言中,将字符串转换为16进制ASCII值是一个常见的操作,尤其是在处理数据编码、网络通信或存储时。这个过程涉及到字符到数字的...理解这些概念和方法,对于在Java开发过程中处理字符串编码问题至关重要。

    JAVA 字符串应用笔记

    以上内容是基于"JAVA 字符串应用笔记"可能涵盖的基本知识点,对于初学者来说,理解和掌握这些概念是进阶学习Java和Android开发的基础。在实际开发中,还会涉及到更多高级特性和实践技巧,如字符串格式化、正则表达式...

    浅谈Java字符串Java开发Java经验技巧共11页.p

    标题中的“浅谈Java字符串Java开发Java经验技巧共11页”表明这是一份关于Java编程,特别是关于Java字符串处理的深度技术文档。在Java中,字符串是编程中经常遇到的基本数据类型,对于任何Java开发者来说,理解和掌握...

    java 各种数据库连接字符串

    连接字符串是JDBC中一个关键的概念,用于指定数据库的详细信息,以便程序能正确地建立到数据库的连接。下面将详细介绍Java连接各种数据库的连接字符串格式。 1. **Oracle** 数据库(thin模式) Oracle数据库的连接...

    Java字符串格式化

    总结来说,Java字符串格式化是构建和展示文本信息的关键技术,包括使用`String.format()`和`printf()`方法,以及`StringBuilder`和`StringBuffer`类。同时,理解并处理GBK这样的特定字符编码对于处理中文字符至关...

    JAVA及相关字符集编码问题

    在深入探讨JAVA与字符集编码问题之前,我们首先需要理解不同字符集编码的基本概念以及它们在JAVA环境中的应用。字符集编码是计算机系统中表示文字的一种方式,它决定了如何将字符转换为二进制数据,以便于存储和传输...

    java字符集

    本文主要探讨了字符编码的基本概念以及Java编程语言如何处理不同字符集。随着信息技术的发展,字符编码技术也在不断演进,以支持全球范围内各种语言的文本表示需求。文章通过“中文”二字作为示例,介绍了几种常见的...

    Java中字符串与byte数组之间的相互转换

    默认情况下,Java字符串使用UTF-16编码。相反,从字节数组还原字符串时,同样需要指定或推断正确的字符编码。 **字符串转byte数组** 1. **方法一**:使用`getBytes()`方法。这是最直接的方法,例如`byte[] bytes =...

    java字符集编码问题

    本文旨在深入探讨与Java相关的字符集编码知识,包括但不限于编码的基本概念、几种常见的字符集编码类型及其特点,以及Java如何处理这些字符集编码问题。 #### 二、编码基本知识 1. **ISO 8859-1** ISO 8859-1是...

    JAVA中汉字字符转化为英文字符

    - **Unicode**: 一种国际标准的字符集,用于处理世界上所有语言的文字。 - **ASCII**: 一种早期的字符编码方式,仅支持英文和其他拉丁文字。 - **UTF-8**: 一种常用的Unicode编码方式,可变长度,兼容ASCII,并且能...

    Java中字符集的详细介绍

    Java中的字符集是一个重要的概念,尤其对于处理多语言文本或者跨平台的数据交换至关重要。Java语言内部使用Unicode编码,具体来说是UTF-16格式,这意味着每个`char`类型变量能够表示一个Unicode字符,通常占据两个...

    如何用java实现不同编码方式字符串的转换(包含异常处理、重复不终止输入、缓冲区bufferedreader的使用)

    总的来说,处理不同编码方式的字符串转换需要理解字符编码的基本概念,熟悉Java的`Charset`类和相关的转换方法,并熟练运用异常处理和`BufferedReader`来实现高效、稳定的操作。通过不断的实践和学习,你将能够更好...

    字符串与Unicode互转工具

    本文将深入探讨“字符串与Unicode互转工具”所涉及的关键知识点,包括字符串的基本概念、Unicode编码体系以及Delphi编程语言中的实现。 首先,让我们了解字符串。在计算机科学中,字符串是由字符组成的序列,可以是...

    JAVA字符集

    Java字符集是一个涵盖编码基础知识、Java编程环境与字符编码关系以及不同编码标准如何在Java中应用的主题。在本文中,我们将深入探讨这些方面,以便更好地理解Java如何处理各种字符编码。 首先,我们要明白编码的...

    JAVA字符串编解码问题.pdf

    Java中的字符串处理涉及到字符编码,这是Java开发中一个重要的概念,因为不同的编码方式会影响字符串的存储和转换。在Java中,所有的字符串(String类型)都是以Unicode编码存储的,这是一种广泛支持的多字节字符集,...

    JAVA字符串编解码问题[定义].pdf

    Java字符串的编解码涉及到字符集(Charset)的概念,字符集是用于表示文本的一套符号及其对应的数值。在Java中,所有的字符串都是基于Unicode编码的,Unicode是一种包含几乎世界上所有字符的标准编码,它保证了不同...

    Unicode和字符串

    在编程世界中,字符编码是基础且至关重要的概念,尤其是Unicode和字符串的处理。Unicode是一个全球统一的字符集,旨在包含世界上所有语言的字符,而字符串则是这些字符的有序组合。本文将深入探讨Unicode和字符串的...

    比较字符串是否相同.rar

    1. **字符串的基本概念** 字符串是编程中的一个基本数据类型,它是由一个或多个字符组成的序列。在大多数语言中,字符串都是不可变的,意味着一旦创建,就不能改变其内容。 2. **字符串比较方式** - **相等比较:...

    0字符串知识点[定义].pdf

    因此,Java选择了Unicode编码标准,这是一种全球性的字符集,能表示世界上几乎所有的语言字符,每个Unicode字符占用16位,即两个字节,这使得Java能够处理各种语言的文本。 在Java中,String是一个不可变的对象,...

    java基础String类选择题练习题

    - **例题**:选择题中可能会问到关于字符串池的概念以及如何利用这一点来提高程序性能。 #### 2. 字符串比较 - **知识点**:讲解如何正确地比较两个字符串是否相等,包括`==`和`equals()`方法的区别。 - **例题**:...

Global site tag (gtag.js) - Google Analytics