Java基礎之終於弄懂了字節、字符、字符集和字符編碼

目錄

一、如何理解概念

二、亂碼的真相

三、如何轉換

四、總結

 


 

關於字節、字符、字符集和字符編碼的問題,根據以下幾篇文字,做以一下整理,原文鏈接如下:

https://blog.csdn.net/longwen_zhi/article/details/79704687

https://blog.csdn.net/softwarenb/article/details/51994943

https://blog.csdn.net/kdc18333608478/article/details/70214493

一、如何理解概念

字節

     也就是Byte,是一個計量單位。數據存儲是以字節爲單位的,也就是常說的1KB、1M、1G等,而數據傳輸是以“位”(bit)爲單位,一個位也就是二進制中的0或1,每8個位組成一個字節,對應該關係也就是1Byte=8bit。

字符

    字符是字母、標點符號、數字的統稱,漢字也是一種字符,是一種表現形式,字符在計算機上也是以二進制的形式存儲的,單位是字節。 一個字符需要多少字節存儲,和字符集編碼有關。

  • 在 ASCII 編碼中,一個英文字母字符存儲需要1個字節。
  • 在 GB2312 編碼或 GBK 編碼中,一個漢字字符存儲需要2個字節。
  • 在UTF-8編碼中,一個英文字母字符存儲需要1個字節,一個漢字字符儲存需要3到4個字節。

字符集

    字符集是多個字符的集合,字符集種類較多,每個字符集包含的字符個數不同,常見字符集有:ASCII字符集、ISO 8859字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。

字符編碼

  1.  計算機要準確的處理各種字符集文字,需要進行字符編碼,以便計算機能夠識別和存儲各種文字。 
  2.  字符編碼(encoding)和字符集不同。字符集只是字符的集合,不一定適合作網絡傳送、處理,有時須經編碼(encode)後才能應用。如Unicode可依不同需要以UTF-8、UTF-16、UTF-32等方式編碼。 
  3. 字符編碼就是以二進制的數字來對應字符集的字符。 因此,對字符進行編碼,是信息交流的技術基礎。

二、亂碼的真相

字符集:

ASCII

         我們知道,計算機內部,所有信息最終都是一個二進制值。每一個二進制位(bit)有0和1兩種狀態,因此八個二進制位就可以組合出256種狀態,這被稱爲一個字節(byte)。也就是說,一個字節一共可以用來表示256種不同的狀態,每一個狀態對應一個符號,就是256個符號,從00000000到11111111。上個世紀60年代,美國製定了一套字符編碼,對英語字符與二進制位之間的關係,做了統一規定。這被稱爲 ASCII 碼,一直沿用至今。

       ASCII 碼一共規定了128個字符的編碼,比如空格SPACE是32(二進制00100000),大寫的字母A是65(二進制01000001)。這128個符號(包括32個不能打印出來的控制符號),只佔用了一個字節的後面7位,最前面的一位統一規定爲0。

ASCII編碼演變過程

        英語用128個符號編碼就夠了,但是用來表示其他語言,128個符號是不夠的。比如,在法語中,字母上方有注音符號,它就無法用 ASCII 碼錶示。於是,一些歐洲國家就決定,利用字節中閒置的最高位編入新的符號。比如,法語中的é的編碼爲130(二進制10000010)。這樣一來,這些歐洲國家使用的編碼體系,可以表示最多256個符號。

        但是,這裏又出現了新的問題。不同的國家有不同的字母,因此,哪怕它們都使用256個符號的編碼方式,代表的字母卻不一樣。比如,130在法語編碼中代表了é,在希伯來語編碼中卻代表了字母Gimel (ג),在俄語編碼中又會代表另一個符號。但是不管怎樣,所有這些編碼方式中,0--127表示的符號是一樣的,不一樣的只是128--255的這一段。

      至於亞洲國家的文字,使用的符號就更多了,漢字就多達10萬左右。一個字節只能表示256種符號,肯定是不夠的,就必須使用多個字節表達一個符號。比如,簡體中文常見的編碼方式是 GB2312,使用兩個字節表示一個漢字,所以理論上最多可以表示 256 x 256 = 65536 個符號。

GBK的由來

       由於ASCII編碼不支持中文,因此,當中國人用到計算機時,就需要尋求一種編碼方式來支持中文。於是,國人就定義了一套編碼規則:當字符小於127位時,與ASCII的字符相同,但當兩個大於127的字符連接在一起時,就代表一個漢字,第一個字節稱爲高字節(從0xA1-0xF7),第二個字節爲低字節(從0xA1-0xFE),這樣大約可以組合7000多個簡體漢字。這個規則叫做GB2312
        但是由於中國漢字很多,有些字無法表示,於是重新定義了規則:不在要求低字節一定是127之後的編碼,只要第一個字節是大於127,就固定表示這是一個漢字的開始,不管後面跟的是不是擴展字符集裏的內容。這種擴展之後的編碼方案稱之爲GBK標,包括了GB2312的所有內容,同時新增了近20000個新的漢字(包括繁體字)和符號。 但是,中國有56個民族,所以,我們再次對編碼規則進行了擴展,又加了近幾千個少數民族的字符,於是再次擴展後得編碼叫做GB18030。中國的程序員覺得這一系列編碼的標準是非常的好,於是統統稱他們叫做"DBCS"(Double Byte Charecter Set 雙字節字符集)。
 

Unicode

         正如上一節所說,世界上存在着多種編碼方式,同一個二進制數字可以被解釋成不同的符號。因此,要想打開一個文本文件,就必須知道它的編碼方式,否則用錯誤的編碼方式解讀,就會出現亂碼。爲什麼電子郵件常常出現亂碼?就是因爲發信人和收信人使用的編碼方式不一樣。可以想象,如果有一種編碼,將世界上所有的符號都納入其中。每一個符號都給予一個獨一無二的編碼,那麼亂碼問題就會消失。這就是 Unicode,就像它的名字都表示的,這是一種所有符號的編碼。

     Unicode 當然是一個很大的集合,現在的規模可以容納100多萬個符號。每個符號的編碼都不一樣,比如,U+0639表示阿拉伯字母Ain,U+0041表示英語的大寫字母A,U+4E25表示漢字嚴。具體的符號對應表,可以查詢unicode.org,或者專門的漢字對應表。
Unicode 的問題:Unicode 只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲。只是一個符號集,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲。每個字符必須使用2個字節甚至更多的字節數,即用16位二進制來表示所有的字符,對於ASCII編碼表裏的字符,保持其編碼不變,只是將長度擴展到了16位,其他國家的字符全部統一重新編碼。由於傳輸ASCII表裏的字符時,實際上可以只用一個字節就可以表示,所以,這種編碼方案在傳輸數據比較浪費帶寬,存儲數據比較浪費硬盤。所以急需一種統一更有效的編碼規則,UTF應運而生。

UTF-8

UTF-8 最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節表示一個符號,根據不同的符號而變化字節長度。
UTF-8 的編碼規則很簡單,只有二條:

  1. 對於單字節的符號,字節的第一位設爲0,後面7位爲這個符號的 Unicode 碼。因此對於英語字母,UTF-8 編碼和 ASCII 碼是相同的。
  2. 對於n字節的符號(n > 1),第一個字節的前n位都設爲1,第n + 1位設爲0,後面字節的前兩位一律設爲10。剩下的沒有提及的二進制位,全部爲這個符號的 Unicode 碼。
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
————————————————


        utf-8區分每個字符的開始是根據字符的高位字節來區分的,比如用一個字節表示的字符,第一個字節高位以“0”開頭;用兩個字節表示的字符,第一個字節的高位爲以“110”開頭,後面一個字節以“10開頭”;用三個字節表示的字符,第一個字節以“1110”開頭,後面倆字節以“10”開頭;用四個字節表示的字符,第一個字節以“11110”開頭,後面的三個字節以“10”開頭。

       其他實現方式還包括 UTF-16(字符用兩個字節或四個字節表示)和 UTF-32(字符用四個字節表示)

ISO8859-1

IS0-8859-1是Java網絡傳輸使用的標準字符集,屬於單字節編碼,最多能表示的字符範圍是0-255,應用於英文系列。比如,字母'a'的編碼爲0x61=97。很明顯,iso8859-1編碼表示的字符範圍很窄,無法表示中文字符。但是,由於是單字節編碼,和計算機最基礎的表示單位一致,所以很多時候,仍舊使用iso8859-1編碼來表示。而且在很多協議上,默認使用該編碼。比如,雖然"中文"兩個字不存在iso8859-1編碼,以gb2312編碼爲例,應該是"d6d0 cec4"兩個字符,使用iso8859-1編碼的時候則將它拆開爲4個字節來表示:"d6 d0 ce c4"(事實上,在進行存儲的時候,也是以字節爲單位處理的)。而如果是UTF編碼,則是6個字節"e4 b8 ad e6 96 87"。很明顯,這種表示方法還需要以另一種編碼爲基礎。Latin1是ISO-8859-1的別名,有些環境下寫作Latin-1。

三、如何轉換

1、UTF-8和Unicode轉換
     比如漢字“智”,utf-8編碼是“\xe6\x99\xba”對應的二進制爲:“111001101001100110111010”,由於utf-8中一個漢字是3個字節,所以對應的模板爲“0000 0800-0000 FFFF |  1110xxxx 10xxxxxx 10xxxxxx”。

11100110  10011001    10111010
1110xxxx  10xxxxxx    10xxxxxx
0110      011001      111010

0110011001111010代表十六進制667A,因此根據規則轉換得出“智”Unicode的位置爲爲“667A”。

同樣,根據Unicode中字符的編碼位置,也能找到對應的utf-8編碼。

2、Unicode與GBK編碼的轉換
    比如漢字“路”,在gbk中的編碼爲“\xc2\xb7”,對應的二進制爲:“1100 0010 1011 0111”。同時“路”在Unicode字符集中的位置是“\u8def”(python中的Unicode類型),因此可以通過“\u8def”在Unicode字符集中找到“路”對應的編碼爲“4237”,對應的二進制爲:“0100 0010 0011 0111”,由於gbk的倆個字節的高字節是爲了區分中文和ASCII,所以將“1100 0010 1011 0111”高字節的“1”去掉後,就對應Unicode字符集中的0100 0010 0011 0111”

3、UTF-8和Unicode與GBK的關係
utf-8--------decode(解碼)----->>Unicode類型<<-------decode(解碼)-----gbk

utf-8<<--------encode(編碼)----->>Unicode類型<<-------encode(編碼)----->>gbk


四、總結

 整理完,寫個小總結:

     隨着計算機的不段發展,因爲地域語音的差異,出現了適應不同語言表示的字符集,而不同的字符集也有不同的編碼規則,文中梳理的最基礎的字符集ASCII,其他字符集都是依據此進行的演變,也隨之衍生出對應的編碼規則和方式,這也就是造成亂碼的真正原因。

   如何避免亂碼問題以及遇到亂碼問題該如何分析:

  • 保證數據的出口和入口採用的編碼方式一致,如果不一致要轉換編碼方式。
  • 有時候系統默認編碼或者應用默認編碼也會是造成亂碼的原因,比如:Windows默認編碼是GBK,Linux的默認編碼爲UTF-8。
  • UTF-8是個不錯的選擇

 

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