java中的char类型和Unicode编码

java中的char类型和Unicode编码

分类: 拿来主义   97人阅读  评论(0)  收藏  举报

学过C++的都知道,在C++里面char是占一个字节的。

Java的char型是非常独特的,占用两个字节,因为Java中char型采用了Unicode编码。

要理解这个问题,我们必须要理解什么是Unicode。

世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。

Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。与ASC2和ISO-8859-1类似,Unicode是一种编码方式,但是它所包括字符的范围却与之前的所有编码方式有着天壤之别。Unicode是一个囊括了几乎世界上所有文字的字符编码表。它的目标是任何文字都可以在其中找到唯一的编码,例如0041表示了字符A,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字“严”。它所要解决的主要问题是:不同语言和地区之间字符编码转换的问题,如果采用了Unicode编码的话则不需要在不同的字符集之间切换,因为都包括在Unicode当中。

Unicode 的实现方式不同于编码方式。一个字符的 Unicode 编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对 Unicode 编码的实现方式有所不同,具体由UTF(UCS Transformation Format)规范规定,常见的UTF规范包括UTF-8、UTF-16、UTF-32。

Java语言中char类型采用UTF-16编码格式。从JDK1.5开始,代码点(code point)是指与一个编码表中某个字符对应的代码值。在Unicode标准中,代码点采用16进制数写,并加上前缀U+,例如U+0041就是字母A的代码点。Unicode代码点可以分成17个代码级别(code plane)。第一个代码级别称为基本的多语言级别(basic multilingual plane),代码点从U+0000到U+FFFF,其中包括了经典的Uncode代码;其余16个附加级别,代码点从U+10000带U+10FFFF,其中包括了一些辅助字符(supplementary character)。UTF-16它采用不同长度的编码表示所有的Unicode代码点(是一种变长的编码方式)。在基本的多语言级别中,每个字符用16位表示,通常被称为代码单元(code unit);而辅助字符采用一对连续的代码单元惊醒编码。这样构成的代码值一定落入基本的多语言级别中空闲的2048字节内,通常被称为替代区域(surrogate area)(U+D800到U+DBFF用于第一个代码单元,U+DC00到U+DFFF用于第二个代码单元)。这种设计十分巧妙,我们可以从中迅速的知道一个代码单元是一个字符编码,还是一个辅助字符的第一或第二部分。强烈建议不要再程序中使用char类型,除非确实需要对UTF-16代码单元进行操作。最好将需要处理的字符串用抽象数据类型表示。可以采用转义序列符\u表示Unicode代码单元。

 

Java的字符类型采用的是UTF-16编码方式对Unicode编码表进行表示。其中一个char类型固定2Bytes(16bits)。

首先先介绍一些Unicode专业术语以及Unicode编码表和UTF-16编码算法的知识:

1.Unicode编码表的专业术语:

(1). 代码点 (code point): 指在Unicode编码表中一个字符所对应的代码值。如汉字“一”的代码点是U+4E00,英文字母“A”的代码点是U+0041。

(2).代码单元( code unit): 规定16bits的存储容量就是一个代码单元。

2.Unicode编码表 分为17个代码级别 (code plane),其中代码点/u0000-/uFFFF为第一级别 ——基本多语言级别 (basic multilingual plane),可以用一个代码单元存储一个代码点。其余16个附加级别 从0x10000-0x10FFFF(需要两个代码单元)。其中需要指出的是在多语言级别中,U+D800-U+DFFF这2048值没有表示任何字符,被称为Unicode的替代区域(surrogate area)。UTF-16正是的运用了这一区域,用2个代码单元(2*16bits)巧妙的表示出20bits代码点的Unicode附加级别。

3.UTF-16编码算法:假设U是一个代码点,也就是Unicode编码表中一个字符所对应的Unicode值。

       (1) 如果U<U+10000,也就是处于Unicode的基本多语言级别中。这样16bits(一个代码单元)就足够表示出字符的Unicode值。
       (2) 如果U+10FFFF>U>=U+10000,也就是处于附加级别中。UTF-16用2个16位来表示出了,并且正好将每个16位都控制在替代区域U+D800-U+DFFF 中了,具体操作如下:
            分别初始化2个16位无符号的整数 —— W1和W2。其中W1=110110yyyyyyyyyy(0xD800-0xDBFF),W2 = 110111xxxxxxxxxx(0xDC00-OxDFFF)。然后,将U的高10位分配给W1的低10位,将U的低10位分配给W2的低10位。这    样就可以将20bits的代码点U拆成两个16bits的代码单元。而且这两个代码点正好落在替代区域U+D800-U+DFFF中。

   具体举个例子:代码点U+1D56B(一个整数集的算术符号Z)

  0x1D56B= 0001 1101 0101 0110 1011

         将0x1D56B的高10位0001 1101 01分配给W1的低10位组合成110110 0001 1101 01=0xD875
         将0x1D56B的低10位01 0110 1011分配给W2的低10位组合成110111 01 0110 1011=0xDD6B

       这样代码点U+1D56B采用UTF-16编码方式,用2个连续的代码单元U+D875和U+DD68表示出了。

 

Java的char类型是固定16bits的。代码点在U+0000 — U+FFFF之内到是可以用一个char完整的表示出一个字符。但代码点在U+FFFF之外的,一个char无论如何无法表示一个完整字符。这样用char类型来获取字符串中的每一个字符就有问题了(针对那些含有代码点在U+FFFF之外的字符串)。我们还用U+1D56B举个例子:
       假设程序中有一个字符串String str = new String(Character.toChars(0x1D56B)),str只有一个字符。注意我们不能用转义字符'/u1D56B'来表示,'/uXXXX'只能表示四位16进制数,也就是只能表示基本多语言级别的Unicode代码点。对于附加级别的代码点,我们只能用上面那种形式表示。
       此时,char ch=str.charAt(0); 这个时候我们返回的结果是第一个代码单元,也就是代码点0xD875(上面已经讲过了U+1D56B采用UTF-16编码方式分成了U+D875和U+DD6B,这样就需要两个char的容量来存储这个字符)。这种方法我们永远无法得到字符串中的这个附加级别字符。
       我们可以这样来精确表示整个字符串中的每一个字符
       int cp=str.codePointAt(0);
       if(Charcter.isSupplementaryCodePoint(cp)) //判断Unicode 代码点是否在附加级别字符范围内。
       i+=2;
       else i++;
       另外,String类中的length()方法也是对字符串进行char统计,也就是计算代码单元数量(代码单元有可能大于正真的字符数量)。如果要计算代码点数量,必须用str.codePointCount(0,str.length())方法。
       由此可见,用char类型来处理字符串在有些情况下是不准确的,我们最好使用字符串类型中处理代码点的方法,不要使用处理char类型(一个代码单元)的方法。 
   
       我顺便提一下,虽然我们的很多系统支持Unicode编码表,但是这并不意味这我们能显示一些字符。比如要显示上面那个字符串System.out.println(str);显示的就是一个'?',这主要是因为字符的显示需要有显示字库的支持,否则还是屏幕上什么都看不到。
 
附:有这样一个问题
      char c='编';  // 我们都知道c占用2个字节,这毫无疑问。
      String str=“ 编 ”;  

      byte[] bytes=str.getBytes(); //这个打印出来,在不同的操作系统上可能有不同的长度。为什么呢?
 
      我们可以去看看API, String的getBytes()方法使用平台的默认字符集将此 String 编码为 byte 序列 。关于默认的字符集可以看看这篇文章《Java字符串与字符集的基本概念 》。
      也就是说,你当前的OS默认支持的字符集使用的是UTF-8,则是3个字节。如果是gb2312/gbk,则2个字节。如果使用unicode/UTF-16则4个字节(开头的两个字节是一个mark)。
      千万不要认为char的占用字节数变了,注意Java标榜自己的一个重要性能就是平台无关性。固定长度的基本数据类型在Java中是永远占固定长度的,这一点和C有很大区别。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值