徹底搞懂這煩人的編碼與亂碼!

我們平時在處理文本文件或者網絡請求時,時不時會遇到亂碼的情況,這篇文章就帶你徹底搞懂編碼和亂碼

首先,我們要知道,在計算機中,一切都是用0和1來表示的。普通的txt文件、或者客戶端發過來的數據等等,這些一切其實都是通過0和1轉化而來的。「那它是怎樣從0和1轉化我們人能看懂的字母或漢字呢?」

ASCII

起初,計算機是由美國人發明的,而且那時候基本就在美國運行,因此,開始只考慮到了美國的需求,0和1只要能轉化成英文字母和符號就行了,大概需要128個字符,所以就規定了0和1轉化爲這128個字符的規則,稱作「ASCII編碼」

計算機存儲的最小單位是byte,而1byte有8個bit位,足夠表示128個字符(「第一位用0表示,剩下7位從0-127分別表示128個字符」),因此ASCII編碼就規定1byte對應一個英文字符,對應關係如下圖:

雖然ASCII編碼對於美國來說足夠用了,但是其他國家顯然是不夠的,於是,各個國家就發明了自己的編碼方式,比如我們「中國的GBK和GB2312等」。但是各個國家自己的編碼也都會兼容ASCII編碼(畢竟人家是鼻祖),前面說到ASCII編碼的第一位爲0,則其他國家自己的編碼都會定義第一位爲1,這樣就能區分當前是ASCII編碼還是自己國家的編碼。

中國的編碼

對於美國而言,一個字節就能表示所有的字符,但是在中國就不行了,光常見的漢字就有好幾千個了,還不包括符號,因此,中國首先出現的編碼形式是「GB2312」,它使用兩個字節來表示,這種編碼主要表示的是簡體中文,不包括繁體字。

後來在GB2312的基礎上,又發明了「GBK」編碼(「GB2312表示的字符,用GBK顯示完全一樣」),增加了一萬多個漢字,裏面包括繁體字,這就是我們日常使用最常見的國內編碼。此編碼也是使用兩個字節來表示。

在這裏提一下,怎麼看一個字符使用了幾個字節,可以使用文本編輯器,我這裏使用的是EmEditor,當然,其他的也行,如果是nodepad++則需要裝一個插件查看二進制。

打開EmEditor,雙擊右下角的編碼,選擇GB2312

接着輸入一個字符,然後再雙擊右下角的編碼,選擇二進制(十六進制)視圖。

可以看到,使用的是兩個字節。這裏再提一點,「爲啥查看二進制,很多編輯器默認都是顯示的是十六進制」?爲啥不直接顯示二進制,這是因爲,一個字節有8個bit位,而一個十六進制數字能表示4個bit位,所以一個字節剛好可以使用兩個十六進制來表示,就像上面顯示的“B9 FA",兩個表示一個字節,既簡單又直觀,如果直接使用二進制,要顯示十六個0或1,眼都要看花掉。

因此,查看字符的二進制數據,最好是通過十六進制來查看。

世界統一編碼

世界上有很多國家,每個國家都有自己特有的編碼方式,這就導致了編碼不統一,容易造成亂碼的情況。那有什麼方式可以讓編碼統一起來呢?可以通過「Unicode」

Unicode和其他編碼方式不一樣,它沒有規定字符對應的二進制,也沒有規定每個字符佔多少個字節,「它只做了一件事,就是給世界上所有的字符分配了一個唯一的數字編號」,就像人的身份證一樣,這是唯一的,它的範圍從0x000000到0x10FFFF之間(十六進制以0x開頭,二進制以0b開頭,八進制以0開頭),這個編號一般寫成十六進制,前面再加上\u。例如”國“的Unicode就是”\u56fd“。

那這些編號怎麼對應到二進制呢?可以採用UTF-8或者UTF-16和UTF-32等。我們平時使用的最多的就是「UTF-8」

UTF-8使用的字節跟Unicode編號的大小有關,編號越大,使用的字節越多,字節個數在1-4之間。對於大部分漢字而已,一箇中文字符需要三個字節。而且UTF-8兼容ASCII。

亂碼

我們平時出現亂碼主要有三種情況。

「第一種」:用文本編輯器打開一個文本文件時,顯示亂碼。

這種情況是因爲,我們平時有時候文本編輯器設置了固定UTF-8編碼,但是突然打開一個GBK編碼個文件,這時候就會出現亂碼,裏面只有漢字會亂碼。

解決方法:只要將編輯器的編碼方式設置成對應的即可。

「第二種」:在程序中讀取文件,出現亂碼。

這種情況是因爲,在程序中讀取文件一般都會設置編碼,有時候省略編碼就會讀取系統的默認編碼,如果設置的編碼和文件的編碼對應不上就會出現亂碼。

解決方法:讀取文件時設置的編碼和文件編碼保持一致。

「第三種」:接收請求數據出現亂碼。

網絡中傳輸的數據也都是二進制數據,所以在我們傳輸數據的時候,發送方指定一種編碼將數據轉成二進制通過網絡傳輸到接收方,接收方再指定一種編碼將二進制數據轉成字符數據。如果發送方和接收方指定的編碼不一致,則會出現亂碼情況。

解決方法:發送方和接收方採用統一編碼即可,一般都是採用UTF-8。

注意:不論是字符串轉二進制,還是二進制轉字符串都需要指定編碼,例如Java中字符串的getBytes()方法將字符串轉成字節數組,雖然方法可以不傳編碼格式,但內部會傳入系統默認編碼Charset.defaultCharset().name()。又比如String(byte bytes[], String charsetName)方法將字節數組轉成字符串,也依然需要傳入編碼格式,如果不傳就會採用系統默認編碼。


掃一掃,關注我


Java面試系列-線程相關(一)

2020-09-03

Java到底是引用傳遞還是值傳遞

2020-08-07

事務:不好意思,你被隔離了!

2020-07-23

spring事務咋和新冠病毒一樣,還會傳染?

2020-07-05

數據是怎麼一步一步到服務器的

2020-06-18


本文分享自微信公衆號 - pipi蛋(pipidan_fuyun)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

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