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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章