編碼是什么?
編碼统求,簡單的理解就是計(jì)算機(jī)中存儲數(shù)據(jù)的格式检碗;類似于現(xiàn)實(shí)世界中物質(zhì)都是由分子、原子組成码邻,在計(jì)算機(jī)中數(shù)據(jù)都是以0/1來進(jìn)行保存折剃,所以為了把0/1轉(zhuǎn)換為人類可以理解的內(nèi)容就需要編碼來進(jìn)行轉(zhuǎn)換。
單位
位(bit):0 or 1像屋;字節(jié)(byte):8個(gè)0 or 1組成怕犁。
常用編碼
ASCII(American Standard Code for Information Interchange):只支持基礎(chǔ)拉丁字符;用一個(gè)字節(jié)表示一個(gè)字符,保證最高位永遠(yuǎn)為'0'奏甫,故可以表示128個(gè)字符 碼表戈轿。
EASCII:因?yàn)闅W洲德語等語言會用到派生拉丁字符;128是不夠用的扶檐,所以就啟用了'1'開頭的另128個(gè)凶杖,'0'開頭與ASCII保存一致。
但這些對世界上其他語言漢語款筑、日語智蝠、韓語是不夠用的,需要多個(gè)字節(jié)奈梳。
GBK系列:為了解決中文編碼問題杈湾,編寫了GBK編碼集,其兼容ASCII攘须,需要注意的是不同的編碼集會存在兼容問題漆撞,GBK一個(gè)漢字使用兩個(gè)字節(jié)表示。
雖然GBK解決了中文編碼問題于宙,但是如果中國用自己開發(fā)的編碼集浮驳,日本、韓國也用自己的捞魁,這樣在信息交互時(shí)如果對方的計(jì)算機(jī)沒有對應(yīng)的編碼集解碼出的數(shù)據(jù)就是錯(cuò)誤的至会,能不能開發(fā)一套世界通用的編碼集呢,Unicode應(yīng)運(yùn)而生谱俭。
Unicode:該編碼集采用4個(gè)字節(jié)表示一個(gè)字符奉件;可以容納世界上所有的字符;但問題也很明顯昆著,假設(shè)要傳一篇英文文檔县貌,使用ASCII編碼與使用Unicode的傳輸量相差4倍,換句話說Unicode傳輸效率太低凑懂;為了解決這個(gè)問題煤痕,出現(xiàn)了UTF-8,它是Unicode的一種實(shí)現(xiàn)方式接谨。
其規(guī)則是:
- 單字節(jié)字符杭攻,字符第一位是0,與ASCII保持一致疤坝。
- 對于多字節(jié)字符(n<=4),第一個(gè)字節(jié)前n位設(shè)置為1馆铁,n+1位設(shè)置為0跑揉,其余字節(jié)前兩位設(shè)置為10。
Unicode范圍 | UTF-8編碼 |
---|---|
單字節(jié):0000 0000 - 0000 007F | 0xxxxxxx |
雙字節(jié):0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx |
三字節(jié):0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
四字節(jié):0001 0000 - 001F FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
'王'字的Unicode編碼是'\u738B',換算成二進(jìn)制是'00000000 00000000 01110011 10001011',處于三字節(jié)范圍,使用UTF-8編碼是'1110111 10001110 10 001011'。
Unicode編碼規(guī)范下有UTF-8,UTF-16,UTF-32三種具體實(shí)現(xiàn)历谍。
UTF-32每個(gè)字符都使用4字節(jié)表示现拒。
UTF-8,采用變長技術(shù)望侈,占用1到4字節(jié)印蔬,兼容ASCII編碼,漢字占用3個(gè)字節(jié)脱衙。
UTF-16統(tǒng)一采用兩個(gè)字節(jié)表示一個(gè)字符侥猬。
java如何編碼
I/O操作中的編碼
I/O包括磁盤和網(wǎng)絡(luò)I/O,從磁盤到內(nèi)存時(shí)使用StreamDecoder來把字節(jié)轉(zhuǎn)換為字符;從內(nèi)存到磁盤時(shí)使用StreamEncoder將字符編碼成字節(jié)捐韩。
內(nèi)存中操作編碼
//String轉(zhuǎn)換到字節(jié)方法
byte[] b = str1.getBytes("UTF-8");
String s = new String(b, "UTF-8");
//Charset提供的byte[]與char[]之間的相互轉(zhuǎn)換
Charset charset = Charset.forName("UTF-8");
ByteBuffer encode = charset.encode("a");
CharBuffer decode = charset.decode(encode);
//char和byte之間的軟轉(zhuǎn)換退唠,不需要編碼和解碼,只是把一個(gè)16bit的char格式拆分為2個(gè)8bit的byte表示荤胁,僅僅是數(shù)據(jù)類型做了轉(zhuǎn)換瞧预。
ByteBuffer headByteBuffer = ByteBuffer.allocate(1024);
ByteBuffer byteBuffer = headByteBuffer.putChar('c');
String name = "hello,王若行";
//Unicode編碼:68 65 6c 6c 6f 2c 738b 82e5 884c
byte[] iso8859 = name.getBytes("ISO-8859-1");
//68 65 6c 6c 6f 2c 3f 3f 3f
//因?yàn)镮so-8859-1是單字節(jié)編碼,所以把多字節(jié)中文編碼轉(zhuǎn)換為單字節(jié)時(shí)仅政,編碼會丟失垢油,轉(zhuǎn)化為 3f(?)。
byte[] gb2312 = name.getBytes("GB2312");
//68 65 6c 6c 6f 2c cdf5 c8f4 d0d0
//GB2312字符集有一個(gè)碼表來進(jìn)行char到byte的轉(zhuǎn)換(sun.nio.cs.ext.EUC_CN類)圆丹。
byte[] gbk = name.getBytes("GBK");
//68 65 6c 6c 6f 2c cdf5 c8f4 d0d0
//GBK編碼兼容GB2312編碼滩愁。
byte[] utf16 = name.getBytes("UTF-16");
//feff 0068 0065 006c 006c 006f 002c 738b 82e5 884c
//UTF-16編碼對字符采用雙字節(jié)表示,其特點(diǎn)是編碼效率非常高运褪,規(guī)則簡單惊楼,但由于不同處理器對2字節(jié)處理方式不同(Big-endian高位字節(jié)在前,Little-endian低位字節(jié)在前)秸讹,所以需要指明是哪種檀咙,故在最前面添加2字節(jié)BYTE_ORDER_MARK值標(biāo)示。
//雖然UTF-16效率很高璃诀,但其對單字節(jié)范圍內(nèi)字符放大一倍弧可,浪費(fèi)了存儲空間,而且其不能對單個(gè)字符的編碼進(jìn)行效驗(yàn)劣欢,如果中間一個(gè)字符碼值損壞棕诵,后面的碼值都會受影響;UTF-16的特點(diǎn)決定了其只適合在計(jì)算機(jī)內(nèi)部使用,不適合作為數(shù)據(jù)傳輸編碼凿将。
byte[] utf32 = name.getBytes("UTF-32");
//00000068 00000065 0000006c 0000006c 0000006f 0000002c 0000738b 000082e5 0000884c
byte[] utf8 = name.getBytes("UTF-8");
//68 65 6c 6c 6f 2c e78e8b e88ba5 e8a18c
//UTF-8對單字節(jié)范圍內(nèi)字符用一個(gè)字符表示校套,對漢字采用三個(gè)字節(jié)表示。
//UTF-8編碼與GBK和GB2312不同牧抵,不用查碼表笛匙,所以在編碼效率上UTF-8效率更好侨把。
//UTF-8在字節(jié)效率上和編碼安全性上做了平衡,適合網(wǎng)絡(luò)傳輸和文件保存妹孙。
為什么Unicode編碼不用查碼表秋柄?
在Unicode之前,所有字符集都是和具體編碼方案綁定在一起蠢正,直接將字符和最終字節(jié)流對應(yīng)起來骇笔,類似ASCII編碼規(guī)定7bit來編碼ASCII字符集;GB2312以及GBK字符集嚣崭,限定最多2個(gè)字節(jié)來編碼字符笨触,并規(guī)定了字節(jié)序;這樣的編碼系統(tǒng)通常是簡單的查表有鹿,通過代碼頁就可以直接將字符映射為存儲設(shè)置上的字節(jié)流旭旭。
這種方法缺點(diǎn)在于,字符和字節(jié)流耦合得太緊密了葱跋,限制了字符集的擴(kuò)展能力持寄。
Unicode在設(shè)計(jì)上考慮了這一點(diǎn),將字符集和字符編碼方案分離開娱俺。
雖然每個(gè)字符在Unicode字符集中都能找到唯一確定的編號(字符碼),但是決定最終字節(jié)流的是具體字符編碼(UTF-8/UTF-16/UTF-32)稍味。