java char String中涉及到的length字符长度概念的研究

Posted 秋风兮月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java char String中涉及到的length字符长度概念的研究相关的知识,希望对你有一定的参考价值。

要想理解char和String中length,首先就要清楚关于字符编码的一些知识,尤其是关于Unicode相关编码的知识。因为java采用Unicode字符集。

可以参考我之前的文章:Unicode UTF的不同

下面摘抄一部分Java 核心技术 卷I中的内容。
Unicode打破了传统字符编码方法的限制。在Unicode出现之前,已经有许多种不同的标准:美国的ASCII、西欧语言中的ISO8859-1、俄国的KOI-8、中国的GB 18030和BIG-5等。这样就产生了下面两个问题:一个是对于任意给定的代码值,在不同的编码方案下有可能对应不同的字母;二是采用大字符集的语言其编码长度有可能不同。例如,有些常用的字符采用单字节编码,而另一些字符则需要两个或更多字节。
设计Unicodde编码的目的就是要解决这些问题。在20世纪80年代开始启动设计工作时,人们认为两个字节的代码宽度足以能够对世界上各种语言的所有字符进行编码,并有足够的空间留给未来的扩展。在1991年发布了Unicode 1.0,当时仅占用65536个代码值中不到一半的部分。在设计Java时决定采用16位的Unicode字符集,这样会比使用8位字符集的程序设计语言有很大的改进。
十分遗憾,经过一段时间,不可避免的事情发生了。Unicode字符超过了65536个,其主要原因是增加了大量的汉语、日语和韩国语言中的表意文字。现在,16位的char类型已经不能满足描述所有Unicode字符的需要了。
下面利用一些专用术语解释一下Java语言解决这个问题的基本方法。从JDK 5.0开始,代码点(code point)是指与一个编码表中的某个字符对应的代码值。在Unicode标准中,代码点采用十六进制书写,并加上前缀U+,例如U+0041就是字母A的代码点。Unicode的代码点可以分成17个代码级别(code plane)。第一个代码级别称为基本的多语言级别(basic multilingual plane),代码点从U+0000到U+FFFF,其中包括了经典的Unicode代码;其余的16个附加级别,代码点从U+10000到U+10FFFF,其中包括了一些辅助字符(supplementary character)。
UTF-16编码采用不同长度的编码表示所有Unicode代码点。在基本的多语言级别中,每个字符用16位表示,通常被称为代码单元(code unit);而辅助字符采用一对连续的代码单元进行编码。这样构成的编码值一定落入基本的多语言级别中空闲的2048字节内,通常被称作替代区域(surrogate area)[U+D800~U+DBFF用于第一个代码单元,U+DC00~U+DFFF用于第二个代码单元]。这样设计十分巧妙,我们可以从中迅速地知道一个代码单元是一个字符的编码,还是一个辅助字符的第一或第二部分。例如,对于整数集合的数学符号z(不是z,csdn显示特殊字符会保存不成功,这里使用z代替,下同),它的代码点是U+1D56B,并且是用两个代码单元U+D835和U+DD6B编码的。
在Java中,char类型用UTF-16编码描述一个代码单元。
Java中的字符串,从概念上讲就是Unicode字符序列。例如,串“Java\\u2122”由5个Unicode字符J、a、v、a和™。
Java字符串由char序列组成。而char数据类型是一个采用UTF-16编码表示Unicode代码点的代码单元。大多数的常用Unicode字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示。
length方法将返回采用UTF-16编码表示的给定字符串所需要的代码单元数量。因此使用length函数获取的长度可能会与字符串显示出的符号的数量不相等。因为辅助字符(supplementary character)会使用两个char来表示。例如:
String test = "testz";
int n = test.length();  // is 6.
如果想获取实际显示的符合的数量,即得到代码点的数量,可以调用:
int cpCount = test.codePointCount(0, test.length); //is 5.
调用s.charAt(n)将返回位置n的代码单元,n介于0~s.length()-1之间。例如:
char first = test.charAt(0);  // first is 't'
char last = test.charAt(6);   //这个没有实际的char字符表示,只是一个char数值。
int ilast = (int) test.charAt(6); // is 56683
要想得到第i个代码点,应该使用下列语句
int index = test.offsetByCodePoints(0,i);
int cp = test.codePointAt(index);
其中函数 int codePointAt(int index) 返回从给定位置开始或结束的代码点。
函数 int offsetByCodePoints(int startIndex, int cpCount)返回从startIndex代码点开始,位移cpCount后的代码点索引。
函数 int codePointCount(int startIndex, int endIndex)返回startIndex和endIndex-1之间的代码点数量。没有配成对的代用字符将计入代码点。
为什么会对代码单元如此大惊小怪请考虑下列语句:
z is the set of integers
使用UTF-16编码表示使用UTF-16编码表示z需要两个代码单元。调用
char ch = sentence.charAt(1);
返回的不是空格,而是z的第二个代码单元。为了避免这种情况的发生,请不要使用char类型。这太低级了。
如果想要遍历一个字符串,并且依次查看每一个代码点,可以使用下列语句:
int cp = sentence.codePointAt(i);
if(Character.isSupplementaryCodePoint(cp)) i += 2;
else i++;
可以使用下列语句实现回退操作:
i--;
if(Character.isSurrogate(sentence.charAt(i))) i--;
int cp = sentence.codePointAt(i)

以上是关于java char String中涉及到的length字符长度概念的研究的主要内容,如果未能解决你的问题,请参考以下文章

string子串匹配(用string自带函数,不涉及char数组转换)

Java 基础知识点 笔记总结

Java内存分配和String类型的深度解析

java中怎样将char和String相互转换

java内存分配和String类型的深度解析(转)

Java数据结构和算法总结-字符串及高频面试题算法