一、引言
日常復習總結,發現太久不看基礎,很多概念的東西還是給忘了,本文將教你如何快速區分“字符”與“字符集”。
二、基礎知識
2.1 字符
各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。 也就是說,它是一個信息單位,一個數字是一個字符,一個文字是一個字符,一個標點符號也是一個字符。
2.2 字節
字節是一個8bit的存儲單元,取值范圍是0x00~0xFF。 根據字符編碼的不同,一個字符可以是單個字節的,也可以是多個字節的。
2.3 字符集
字符的集合就叫字符集。不同集合支持的字符范圍自然也不一樣,譬如ASCII只支持英文,GB18030支持中文等等。
在字符集中,有一個碼表的存在,每一個字符在各自的字符集中對應着一個唯一的碼。但是同一個字符在不同字符集中的碼是不一樣的,譬如字符“中”在Unicode和GB18030中就分別對應着不同的碼(20013
與54992
)。
簡單來說,字符集就是一個按規則排列的字符的集合,可以理解為現實生活中的各種“字典”。例如:牛津字典,劍橋商務字典等等。同一個“單詞”在不同的字典中的位置是不同的,同理也不是所有的字典都支持各種語言的,例如ASCLL字符集里面就沒有中文。
2.4 字符編碼
定義字符集中的字符如何編碼為特定的二進制數,以便在計算機中存儲(就是將字符在字符集中的對應位置化為二進制)。字符集和字符編碼一般一一對應(也有例外)
字符集與字符編碼的一個例外就是Unicode字符集,它有多種編碼實現(UTF-8,UTF-16,UTF-32等)
三、舉例理解
其實通過上面的標藍的句子相信大家已經有了區分的理解。下面再舉出一些例子,來加深大家的理解:
-
ASCII碼是一個字符集,同時它的實現也只有一種,因此它也可以指代這個字符集對應的字符編碼。
-
GB18030是一個字符集,主要是中國人為了解決中文而發明制定的,由於它的實現也只有一種,所以它也可以指代這個字符集對應的字符編碼。
-
Unicode是一個字符集(java默認),為了解決不同字符集碼表不一致而推出的,統一了所有字符對應的碼,因此在這個規范下,所有字符對應的碼都是一致的(統一碼),但是統一碼只規定了字符與碼表的一一對應關系,卻沒有規定該如何實現,因此這個字符集有多種實現方式(UTF-8,UTF-18,UTF-32),因此這些實現就是對應的字符編碼。 也就是說,Unicode統一約定了字符與碼表直接一一對應的關系,而UTF-8是Unicode字符集的一種字符編碼實現方式,它規定了字符該如何編碼成二進制,存儲在計算機中。
附:常用編碼表
ISO8859-1 | GBK/GBK2312 | UTF-8 | UTF-16 | UTF-32 | |
英文字符 | 1字節 | 2字節 | 大部分3,少部分4 | 2字節 | 4字節 |
中文字符 | 1字節 | 1字節 | 1字節 | 2字節 | 4字節 |
如表所示,ISO8859-1、utf-16、utf-32都是定長字符編碼,其中utf-16(Unicode)更是jvm默認的內碼編碼。
內碼 vs 外碼
java在內部都是unicode編碼,不存在什么GBK/UTF-8只有在輸入輸出的時候,才會發生與外部的編碼轉化問題。
簡單來說,內碼:char或String在內存里使用的編碼方式。
外碼:除了內碼都可以認為是“外碼”。(包括class文件的編碼)
java內碼:unicode(utf-16)
jvm默認外碼:windows——gbk
Linux——utf-8
外碼操作:
[String].getBytes() 就是將內碼轉換成外碼存儲在byte數組里,方法有兩種形式:
-
getBytes(String charsetName): 使用指定的字符集將字符串編碼為 byte 序列,並將結果存儲到一個新的 byte 數組中。
-
getBytes(): 使用平台的默認字符集將字符串編碼為 byte 序列,並將結果存儲到一個新的 byte 數組中。
new String(byte[] bytes, String charset): 將字符序列bytes使用“charset”的字符編碼(外碼),解碼成字符串(內碼)。
編碼和解碼的“字符編碼”必須要一致才能解碼成想要的字符串(英文例外)。
但是為什么在tomcat 下,使用 new String(s.getBytes("iso-8859-1") ,"GBK") 卻可以用呢? 答案是:
tomcat 默認使用iso-8859-1編碼, 也就是說,如果原本字符串 str 是GBK的,tomcat傳輸過程中,將GBK轉成iso-8859-1編碼的字符數組了,相當於:
String s = "中國";
byte[] str_bytes = s.getBytes("GBK"); // 底層操作1:jvm,此時編碼格式為gbk; String s = new String(str_bytes, "iso-8859-1"); // 底層操作2:tomcat; String str_new =new String(s.getBytes( "iso-8859-1"), "gbk"); // 得到正確字符串
默認情況下,使用iso-8859-1讀取中文肯定是有問題的,那么我們需要將iso-8859-1 再轉成GBK, 而iso-8859-1 是單字節編碼的,即他認為一個字節是一個字符, 那么這種轉換不會對原來的字節數組做任何改變,因為字節數組本來就是由單個字節組成的,如果之前用GBK編碼,那么轉成iso-8859-1后編碼內容完全沒變, 則 s.getBytes("iso-8859-1") 實際上還是原來GBK的編碼內容則 new String(s.getBytes("iso-8859-1") ,"GBK") 就可以正確解碼了。 所以說這是一種特殊情況。