Java SE 学习---字符串String

  • 代码点和代码单元
由于Java中的字符串是由char值的序列组成的,而char数据类型是一个采用UTF-16编码表示的Unicode代码点的代码单元。一个代码单元使用16位的空间来表示,这些代码单元对应的代码点的范围是U+0000~~~U+FFFF,在这个范围内能够表示世界上的绝大多数现存的字符,然而由于存在大量的汉语、日语和韩国语言中的表意文字,16位的char类型已经不能描述所有的Unicode字符了。
  1. 代码点(code point): 指在Unicode编码表中一个字符所对应的代码值。如汉字“一”的代码点是U+4E00,英文字母“A”的代码点是U+0041。
  2. 代码单元( code unit): 规定16bits的存储容量就是一个代码单元。
  3. Unicode编码表 分为17个代码级别 (code plane),其中代码点U+0000~U+FFFF为第一级别 ——基本多语言级别 (basic multiling l plane),可以用一个代码单元存储一个代码点。其余16个附加级别 从0x10000-0x10FFFF(需要两个代码单元)。其中需要指出的是在多语言级别中,U+D800-U+DFFF这2048值没有表示任何字符,被称为Unicode的替代区域(surrogate area)。UTF-16正是的运用了这一区域,用2个代码单元(2*16bits)巧妙的表示出20bits代码点的Unicode附加级别。
  • UTF-16计算Unicode附加级别编码的算法
假设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=110110XXXXXXXXXX(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表示出了。
  • String类的常用API
有了上述的关于Java中字符串的编码以及存储的分析之后就不难理解在使用char相关的方法时的一些困惑了。下面解释一下使用String类时常用到的一些API
      1.int length(),length方法返回采用UTF-16编码表示给定字符串所需要的代码单元数量,即存储这个字符串需要多个少个16bits.
          例如:
  String greeting="hello";  int n=greeeting.length();// n is 5
  String greeting2=new String(Character.toChars(0x1D56B));//(上面已经讲过了U+1D56B采用UTF-16编码方式分成了U+D875和 U+DD6B,这样就需要两个char的容量来存储这个字符)
  int m=greeeting2.length();//n is 2


(注意虽然上面的greeting2只有一个字符,我们却不能使用转义字符的方式(\u?)来表示这个字符,这时因为' \uXXXX'只能表示四位16进制数,也就是只能表示基本多语言级别的Unicode代码点。对于附加级别的代码点,我们只能用上面那种形式表示。)
    2.int codePointCount( int beginIndex,int endIndex) ,上面的length方法返回的是字符串占用的代码单元的数量,有时候我们只需要知道字符的个数,那么这个时候代码点的数量就是实际的字符的个数,codePointCount方法就是来检查字符串中代码点的数量的方法。代码点的数量是从beginIndex到endIndex位置的字符的代码点的数量。
int cpCount=greeting2.codePointCount(0, greeting2.length());


       3.char charAt( int index ),返回字符串位置为index的代码单元,index介于0~s.length()-1之间。
temp=greeting2.charAt(i);


上图所显示的程序是输出greeting2的所有代码单元的内容,由于附加级别的字符在经过UFT-16编码后会形成两个基本多语言级别的代码单元,且这两个代码单元的内容是保留的所以显示这两个代码单元的内容是会显示为?
    4.int offsetByCodePoints(int index,int codePointOffset),计算从index开始经过codePointOffset的codePoint的位置,这里如果在codePointOffset范围内包含附加级别的字符那么返回的位置将会是实际的位置(即附加级别的字符会用两个代码点来表示)。int codePointAt(int index) 返回index出的代码点的值

在上图中offsetByCodePoints方法中从0开始偏移0个代码点,显然还是第0个代码点,由于第0个代码点对应的字符是附加级别里面的字符所以当用codePointAt(0)去获取第0个代码点对应的值时,就是字符0x1D56B对应的十进制值。
在上图中offsetByCodePoints方法中从0开始偏移1个代码点,根据对offsetByCodePoints方法的说明应该知道由于字符0x1D56B是附加级别里面的字符在UTF-16编码中使用两个代码点来表示它的,所以从0偏移1个代码点之后会直接跳过字符0x1D56B的第二个代码点显示index为第三个代码点(从0开始计数,所以在上图中index为2),那如果我们要获取字符0x1D56B的第二个代码点怎么办呢?在这个例子中我们可以直接指定codePointAt(int index)的参数index为1即可,如下图所示:

code point值56683(0xDD6B)就是字符0x1D56B经过的UTF-16编码后的第二个代码点的值。Character的静态方法isSupplementaryCodePoint(int codePoint)来检查当前代码点是否是附加级别的字符的代码点,或者判断是附加级别的字符的代码单元的第一部分还是第二部分的代码点(通过对UTF-16编码的分析,这个判断过程就很简单了)。例如通过下面的方法来输出greeting2的所有代码点的值。

    5.lastIndexOf(),这个方法有多个重载方法,值得注意的是lastIndexOf(int cp),返回的是从字符串的尾部开始第一与制定代码点cp相匹配的字符的位置,且位置是以代码单元计算的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值