目錄
今天和夥計萌一起寫JSP的時候,應用到了修改編碼,所以自己也總結一下這個原理,參考了很多寫的不錯的文章。
可能很多編程的夥伴萌學到了,tomcat 和 JSP(java server page)
這裏先引出幾個常見的英文:
-
charset英文釋義就是字符集 ,是服務器把生成的html發佈給客戶端時的編碼,可以任意指定
-
pageEncoding:JSP文件本身的編碼方式
-
GBK(GBK即“國標”、“擴展”漢語拼音的首字母): 全稱《漢字內碼擴展規範》
-
UTF-8:針對Unicode的一種可變長度字符編碼。
一、字節概念
字節(Byte )是計算機用於計量存儲容量的一種計量單位,作爲一個單位來處理的一個二進制數字串,是構成信息的一個小單位。最常用的字節是八位的字節,即八位的二進制數。
比如:11110000(2),這個八位的二進制數在計算機中代表佔用了一個字節。
二、字符概念
字符和字節要區別開,字符指類字形單位或符號: 字母、數字、運算符號、標點符號和其他符號,以及一些功能性符號。
比如: 1 、+ & ?%、我 等都是字符
不同的編碼導致一個字符所佔的內存不同
GBK中一箇中文佔兩個字節(即16位二進制) 而 UTF-8中則佔三個字節(24位二進制)
三、編碼規範(編碼方案)
隨計算機的發展,不同地區語言文字的需要,工程師們希望在計算機中顯示字符,但計算機只能識別0和1組成的的二進制數,於是編碼規範被擴展出來。
例如: ASCII編碼規範,計算機通過規範可以將我們常用的字符和二進制之間相互轉換。
GBK編碼規範,計算機可以通過這套規範,在中文和二進制之間相互轉換。
我們識別字符,計算機識別二進制。
1.字庫表
像我們的大字典一樣,每一頁的第幾行作爲一個地址,對應一個值。
一套編碼規範不一定能包含世界上所有字符,每套編碼規範都有自己的使用場景。而字庫表就存儲了編碼規範中能顯示的所有字符,計算機就是根據二進制數從字庫表中找到字符然後顯示給用戶滴,相當於一個存儲字符的數據庫。
比如: 所有漢字都保存在GBK 編碼規範的字庫表中。所以可以顯示漢字,但法語,俄語並不在其字庫表中,所以GBK不能顯示法語,俄語等不包含在其中的字符。
2.編碼字符集(常說的字符集)
在一個字庫表中,每一個字符都有一個對應的二進制地址,而編碼字符集就是這些地址的集合。
例:在ASCII碼的編碼字符集中,字母A的序號(地址)是65,65的二進制就是01000001。我們可以說編碼字符集就是用來存儲這些二進制數的。而這個二進制數就是編碼字符集中的一個元素,同時它也是字庫表中字母A的地址。我們根據這個地址就可以顯示出字母A。
3.字符編碼(編碼方式)
有了字庫表和編碼字符集的概念,我們就可以直接使用二進制地址來得到字符了。
但直接使用字符對應的二進制地址來顯示文字是十分浪費內存的,Unicode 編碼規範中包括了幾百萬個字符,想要包括幾百萬個不同的字符,起碼需要3個字節的容量,爲了方便將來擴展,Unicode還保留了更多未使用的空間,最多可以存儲4個字節的容量。
如果爲了區分每個字符,00000000 00000000 00000000 00001111這種其實只佔了1個字節的字符,我們要爲他分配4個字節的空間,這就導致一個1G大小的文件,需要4G才能保存,很明顯浪費了不少空間。
於是程序員制定了一套算法來節省空間,而每種不同的算法都被稱作一種編碼方式(下文中爲了便於理解都將使用編碼方式來稱呼字符編碼)。一套編碼規範可以有多種不同的編碼方式,不同的編碼方式有不同的適應場景。
例如:UTF-8就是一種編碼方式,Unicode是一種編碼規範。此外,Unicode還有UTF-16,UTF-32這兩種編碼方式。不同的編碼方式規則不同,節約的空間也不同。
總結:一個較短的二進制數,通過一種編碼方式,轉換成編碼字符集中正常的地址,然後在字庫表中找到一個對應的字符,最終顯示給用戶。
四、常見編碼規範(編碼方案)介紹
1.ASCII碼
(American Standard Code for Information Interchange): 美國信息交換標準代碼。
共包含128個字符即二進制的:00000000~011111111.可以表示阿拉伯數字和大小寫英文字母,以及一些簡單的符號。
2.GBK
GBK全稱《漢字內碼擴展規範》,支持國際標準ISO/IEC10646-1和國家標準GB13000-1中的全部中日韓漢字。GBK字符集中所有字符佔2個字節,不論中文英文都是2個字節。 沒有特殊的編碼方式,習慣稱呼GBK 編碼。
3.Unicode
從以上幾種編碼規範可以看出,各種編碼規範互不兼容,且只能表示自己需要的字符,於是,國際標準化組織(ISO)決定製定一套全世界通用的編碼規範,這就是Unicode。 Unicode包含了全世界所有的字符。Unicode最多可以保存4個字節容量的字符。但是要區分每個字符,每個字符的地址需要4個字節。這是十分浪費存儲空間的,於是,程序員就設計了幾種字符編碼方式,比如:UTF-8,UTF-16,UTF-32。 最廣爲程序員使用的就是UTF-8,UTF-8是一種變長字符編碼,注意:UTF-8不是編碼規範,而是編碼方式。
UTF-8編碼規則如下:
編碼規則表
Unicode 十六進制碼點範圍 | UTF-8 二進制 |
---|---|
0000 0000 ~ 0000 007F | 0xxxxxxx |
0000 0080 ~ 0000 07FF | 110xxxxx 10xxxxxx |
0000 0800 ~ 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000 ~ 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
如上表所示,對於只需要1個字節的字符,UTF-8採用ASCII碼的編碼方式,最高位補0來表示。
例如:01000001我們就是用01000001來表示,對於一個字節的字符,其實就是直接使用地址表示。
而對於n個字節的字符(n>1),即大於一個字節的字符,採用第一個字節前n位補1。第n+1位填0,後面字節的前兩位一律設爲10。剩下的沒有提及的二進制位,全部爲這個符號的unicode碼。
例如:漢字嚴的Unicode碼是4E25轉換成二進制就是01001110 00100101共15位,根據上表可知使用UTF-8字符編碼後佔3個字節,因此前3位是1,第4位(n+1位)是0,後面兩個字節中每個字節的前兩位都是10,即1110 xxxx 10 xxxxxx 10xxxxxx。填充進去後就變成了1110 0100 10 111000 10 100101共計24位佔3個字節。
由此可見,英文在UTF-8字符編碼後只佔1個字節,中文佔了3個字節。 雖然UTF-8編碼沒有GBK編碼佔的空間小,但他勝在面向全世界,至於使用哪一種編碼還是取決於具體的使用環境。
關於英文佔一個字節,中文佔三個字節的驗證:
import java.io.UnsupportedEncodingException;
public class Main {
public static void main(String[] args) throws UnsupportedEncodingException {
//英文
String usa = "x";
encodeAndDecode(usa);
//中文
String chinese="何";
encodeAndDecode(chinese);
}
public static void encodeAndDecode(String init) throws UnsupportedEncodingException {
byte[] bts = init.getBytes("UTF-8");
System.out.print("字符'"+init+"'進行編碼得到的三個字節值:");
for (byte bt : bts)
System.out.print(bt+" ");
System.out.println("\n解碼檢驗******************");
//解碼
String init_utf8 = new String(bts,"UTF-8");
System.out.println(init_utf8+"\n");
}
}
運行結果如下
字符'x'進行編碼得到的三個字節值:120 解碼檢驗 x 字符'何'進行編碼得到的三個字節值:-28 -67 -107 解碼檢驗 何
參考:
- https://blog.csdn.net/qq_42068856/article/details/83792174?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
- https://www.zhihu.com/question/57461614