国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
Java字符串與字符集的基本概念

本作品采用知識共享署名-非商業(yè)性使用-相同方式共享 2.5 中國大陸許可協(xié)議進行許可。

 

 

Java String是Java API中最常用的類,本文和大家談?wù)凷tring類的內(nèi)部原理,同時描述ISO-8859-1字符集在字符串處理中的獨特用處。 

Java字符串的內(nèi)部編碼

String類內(nèi)部管理著一個char類型的數(shù)組,Java API是這樣描述char基本類型的:
char 數(shù)據(jù)類型(和 Character 對象封裝的值)基于原始的 Unicode 規(guī)范,將字符定義為固定寬度的 16 位實體。
這一點我們可以通過下面的語句加以證實:

  1. System.out.println(Character.SIZE); // 結(jié)果為16  

 

根據(jù)String類的構(gòu)造方法,我們可以這樣定義字符串:

  1. String str = "abc";  

 

上句代碼等效于:

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

 

同時,char基本類型與byte、short、int、long一樣可以用數(shù)值表示。所以上述代碼等效于:

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

 

需要注意的是,char基本類型始終使用16位即兩個字節(jié)表示一個字符,即使這個字符的Unicode值小于0xFF(如ASCII碼)。對于Unicode值大于0xFF的字符,如“中國”二字的Unicode編碼分別為/u4E2D和/u56FD,我們可以這樣創(chuàng)建:

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

 

當(dāng)然也可以這樣創(chuàng)建:

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

 

通過上面的描述可以明確兩個要點:

字符串對象中的每一個元素始終占據(jù)兩個字節(jié)長度,一個或兩個元素(增補字符占據(jù)兩個元素)表示一個字符。
字符串對象中的每一個元素都使用Unicode字符集進行編碼。

字符集(Charset)與字節(jié)化字符數(shù)據(jù)

在出現(xiàn) Unicode 規(guī)范之前,計算機在處理字符串的問題上經(jīng)歷過ASCII和ANSI編碼兩個階段,在ASCII時代,計算機只能處理英文數(shù)字以及幾個基本符號,當(dāng)時使用的是單字節(jié)字符集(SBCS)。
各國為了能在計算機上處理本國的文字,制訂了相應(yīng)的國家標(biāo)準(zhǔn),規(guī)定了各自的ANSI編碼。如中文簡體使用GBK標(biāo)準(zhǔn);中文繁體使用BIG5標(biāo)準(zhǔn);日文使用Shift_JIS標(biāo)準(zhǔn)。在ANSI編碼時代,計算機使用多字節(jié)字符集(MBCS)處理文字。如“中國ABC”,在GB2312標(biāo)準(zhǔn)中,“中國”兩個字符分別使用兩個字節(jié)表示,而“ABC”三個英文字符又分別使用一個字節(jié)表示。各國文字的ANSI編碼互不通用,不能使用一種ANSI編碼表達(dá)多個國家的文字。
為了文字交流的順暢,也就是說為了達(dá)到在一個文本當(dāng)中既可以有中文簡體字存在,也可以有中文繁體字存在的目的。國際組織根據(jù)各國語言的特點,使用兩個字節(jié)的數(shù)據(jù)量將大部分國家的文字信息整合到一個字符集中,這就是Unicode編碼,也稱萬國碼。關(guān)于Unicode編碼的特點,前文已經(jīng)描述,那就是使用雙字節(jié)字符集(DBCS)處理文字。


在Java中,使用字節(jié)數(shù)組保存不同字符集的字符值,如使用ASCII字符集保存“abc”的方法如下:

  1. // 使用ASCII編碼的“abc”   
  2. byte[] ascBytes = {(byte)0x61, (byte)0x62, (byte)0x63};  

 

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

  1. // 使用GBK編碼的“中國”   
  2. byte[] gbkBytes = {(byte)0xD6, (byte)0xD0, (byte)0xB9, (byte)0xFA};  

 

當(dāng)然我們也可以使用Unicode字符集保存“中國”二字。如:

  1. // 使用Unicode編碼的“中國”   
  2. byte[] unicodeBytes = {(byte)0x4E, (byte)0x2D, (byte)0x56, (byte)0xFD};  

 

一個特殊的字符集UTF-8是與Unicode規(guī)范對應(yīng)的多字節(jié)表示的字符集。如“中”字的UTF-8編碼為“0xE4, 0xB8, 0xAD”三個字節(jié)。

 

在這里,將這些與具體字符集相對應(yīng)的字節(jié)化的數(shù)據(jù)流稱為字節(jié)化字符數(shù)據(jù),與字符串對象形成鮮明對照的是,字符串對象的最小單位是兩個字節(jié)而字節(jié)化字符數(shù)據(jù)的最小單位則是一個字節(jié)。由此我們可以明確另外兩個要點:

字節(jié)化字符數(shù)據(jù)中的每一個元素始終占據(jù)一個字節(jié)長度,一個或多個元素表示一個字符。
字節(jié)化字符數(shù)據(jù)必須與一個字符集相對應(yīng)。

字節(jié)化字符數(shù)據(jù)與字符串對象的互換

在Java程序運行過程中,字符串對象始終以Unicode編碼方式保存在內(nèi)存中,但將字符串對象保存到持久化資源(文件或數(shù)據(jù)庫)或?qū)⑵渫ㄟ^網(wǎng)絡(luò)傳輸時,通常是以字節(jié)化字符數(shù)據(jù)的方式進行處理。這樣就要求Java API必須提供兩者互換的功能。事實上這一功能在String類及Charset類中已經(jīng)提供。
一方面我們可以利用String類的getBytes()方法返回不同字符集的字節(jié)化字符數(shù)據(jù),其本質(zhì)是從Unicode字符集編碼向其它字符集編碼轉(zhuǎn)換的過程。例如:

  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. }  

 

上例的輸出結(jié)果為:

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

需要注意的是,從字符串對象中取出的Unicode編碼的字節(jié)化字符數(shù)據(jù)時,其開始部分存在一個BOM(ByteOrderMark),一般情況下,該BOM值為“0xFE 0xFF”,即大端字節(jié)序(BIG_ENDIAN)。如果BOM值為“0xFF 0xFE”則為小端字節(jié)序(LITTLE_ENDIAN)。

 

另一方面也可以利用String類的構(gòu)造方法根據(jù)不同字符集的字節(jié)化字符數(shù)據(jù)產(chǎn)生一個字符串對象,其本質(zhì)是從其它字符集編碼向Unicode字符集編碼轉(zhuǎn)換的過程。例如:

  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")));  

 

上例三個輸出語句均輸出“中國”二字。

上述兩種轉(zhuǎn)換過程,特別是Unicode字符集編碼向其它字符集編碼的轉(zhuǎn)換過程中會出現(xiàn)轉(zhuǎn)換失敗的現(xiàn)象。轉(zhuǎn)換失敗時該Unicode碼自動用0x3F代替。例如:

  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. }  

 

上例的輸出結(jié)果為:

中國的BIG5編碼:
0xA4 0xA4 0x3F

 

其中“國”由于沒有繁體中文BIG5字符集對應(yīng)的編碼值,所以會用0x3F表示。

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

ISO-8859-1是單字節(jié)字符集,是ASCII字符集的補充。通常情況下使用ISO-8859-1字符集進行字符串對象與字節(jié)化字符數(shù)據(jù)的互換操作與前述完全一致。例如:

  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. }  


上例的輸出結(jié)果為:

ISO-8859-1編碼:
0x61 0x62 0x63
UNICODE編碼:
0xFE 0xFF 0x00 0x61 0x00 0x62 0x00 0x63

通過此例可以看出,從ISO-8859-1字符集轉(zhuǎn)換成Unicode字符集的過程是將字節(jié)化字符數(shù)據(jù)中的每個一個byte類型元素直接保存成一個char類型元素。也就是說下面的代碼:

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

 

等效于:

  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的轉(zhuǎn)換過程是對編碼值為0x00 - 0xFF之間都有效的一種轉(zhuǎn)換。在ISO-8859-1字符集中,0x00-0x1F、0x7F、0x80-0x9F沒有定義。我們可以使用其中幾個無效編碼進行測試:

  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. }  

 

上例的輸出結(jié)果為:

ISO-8859-1編碼:
0x00 0x1A 0x7F 0x93
UNICODE編碼:
0xFE 0xFF 0x00 0x00 0x00 0x1A 0x00 0x7F 0x00 0x93

根據(jù)這一特點,我們可以總結(jié)出最后一個要點:

利用ISO-8859-1字符集,我們可以將任何一個字節(jié)數(shù)組無損保存到字符串對象中。

也就是說,可以利用這一特點將字節(jié)化字符數(shù)據(jù)的原始字節(jié)數(shù)據(jù)(而不是經(jīng)過Unicode字符集轉(zhuǎn)換之后的數(shù)據(jù))直接保存在字符串對象中。反之也可以從一個經(jīng)過ISO-8859-1編碼的字符串對象中取出原始字節(jié)數(shù)據(jù)。例如:

  1. public static void main(String[] args) {  
  2.     byte[] utf8Bytes = {(byte)0xE4, (byte)0xB8, (byte)0xAD, (byte)0xE5, (byte)0x9B, (byte)0xBD};  
  3.     printBytes("原始字節(jié)流:" , utf8Bytes);  
  4.       
  5.     // ISO-8859-1編碼過程   
  6.     // 保存原始字節(jié)數(shù)據(jù)流(不經(jīng)過Unicode編碼)到字符串對象   
  7.     String isoStr = new String(utf8Bytes, Charset.forName("ISO-8859-1"));  
  8.       
  9.     // ISO-8859-1解碼過程   
  10.     // 從字符串對象中取得與utf8Bytes內(nèi)容完全相等的原始字節(jié)數(shù)據(jù)流   
  11.     byte[] tmp = isoStr.getBytes(Charset.forName("ISO-8859-1"));  
  12.     printBytes("轉(zhuǎn)換字節(jié)流:" , tmp);  
  13. }  
  14.   
  15. public static void printBytes(String title, byte[] data) {  
  16.     // 同上   
  17. }  


上例的輸出結(jié)果為:

原始字節(jié)流:
0xE4 0xB8 0xAD 0xE5 0x9B 0xBD
轉(zhuǎn)換字節(jié)流:
0xE4 0xB8 0xAD 0xE5 0x9B 0xBD

這種通過字符串對象保存原始字節(jié)數(shù)據(jù)的方法被很多地方所使用。最常見的就是Java WEB應(yīng)用中Web服務(wù)器對來自于服務(wù)器的表單數(shù)據(jù)的處理,關(guān)于這方面的詳細(xì)說明請參考 如何解決Java WEB應(yīng)用中的亂碼問題。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
深入分析 Java 中的中文編碼問題
Tomcat關(guān)于encoding編碼的默認(rèn)設(shè)置以及亂碼產(chǎn)生的原因
帶你玩轉(zhuǎn)Visual Studio——帶你理解多字節(jié)編碼與Unicode碼
錕斤拷?UTF-8與GBK互轉(zhuǎn),為什么會亂碼?
java 中String編碼和byte 解碼 總結(jié)
PHP編碼轉(zhuǎn)換和PHP編碼判斷
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服