寫代碼總是莫名其妙的亂碼?這一篇教你從原理出發徹底幹掉它

前言

中文亂碼問題在我們日常開發中司空見慣,那麼亂碼問題是如何產生的呢?又怎樣去解決亂碼問題呢?本文將結合基本概念和例子展開闡述,希望大家有收穫。

一個簡單亂碼的例子

package whx;

import java.io.UnsupportedEncodingException;

public class TestEncodeAndDecode {
    public static void main(String[] args) throws UnsupportedEncodingException {

        String str = "測試中文亂碼";
        byte[] b = str.getBytes("GBK");
        System.out.println(new String (b,"UTF-8"));
    }
}
複製代碼

用GBK編碼,用utf-8解碼,產生亂碼,運行結果如下:

寫代碼總是莫名其妙的亂碼?這一篇教你從原理出發徹底幹掉它

 

相關基礎概念

要理解亂碼的根源,需要先了解清楚位、字節、字符、字符集等相關概念。

位(bit)

位是計算機存儲數據的最小單位,1或者0就表示1位,如10010010就表示8位的二進制數。

字節

字節是計算機信息技術用於計量存儲容量的一種計量單位,作爲一個單位來處理的一個二進制數字串,是構成信息的一個小單位。

1 B = 8 bit (1字節等於8位)
1 KB = 1024 B = 1024 字節
1 MB = 1024 KB 
1 GB = 1024 MB
1 TB = 1024 GB
複製代碼

字符

字符是指計算機中使用的字母、數字、字和符號,是數據結構中最小的數據存取單位。如a、A、B、b、大、+、*、%等都表示一個字符;

在 ASCII 編碼中,一個英文字母字符存儲需要1個字節。
在 GB 2312 編碼或 GBK編碼中,一個漢字字符存儲需要2個字節。
在UTF-8編碼中,一個英文字母字符存儲需要1個字節,一個漢字字符儲存需要3到4個字節。
在UTF-16編碼中,一個英文字母字符或一個漢字字符存儲都需要2個字節
在UTF-32編碼中,世界上任何字符的存儲都需要4個字節
複製代碼

字符集

字符集是多個字符的集合,字符集種類較多,每個字符集包含的字符個數不同。常見字符集名稱:

ASCII字符集
GB2312字符集
Unicode字符集
複製代碼

編碼、解碼

計算機只認識二進制的1和0,而人類都是有自己的語言的,雙方要能進行信息交流,必須要有從文字到0、1的轉化,以及0、1到文字轉化。

編碼: 就是將文本字符轉換成計算機可以識別的0、1機器碼。

解碼: 將存儲在計算機中的二進制數解析成文字、字符。

常見字符集及其編碼方式

常見字符集有ASCII、GBK、Unicode等

ASCII字符集

ASCII字符集:它包括英文字母、阿拉伯數字和西文符號等可顯示字符,以及回車鍵、退格等控制字符。

ASCII 編碼:它是美國製定的字符編碼,用於將英語字符轉化爲二進制,規定了128個字符的編碼。

GBXXXX字符集

GBXXXX系列包括GB2312、GBK、GB18030,適用於漢字處理、漢字通信等系統之間的信息交換。

GB2312

  • 全稱是《信息交換用漢字編碼字符集》,支持六千多漢字。
  • 國家簡體中文字符集,兼容ASCII,中國大陸和新加坡都採用此編碼。
  • 每個漢字及符號以兩個字節來表示。
  • 高字節從A1~F7, 低字節從A1~FE。將高字節和低字節分別加上0XA0即可得到編碼。

GBK

  • GBK全稱《漢字內碼擴展規範》,擴展了GB2312,加入對繁體字的支持,支持兩萬多漢字。
  • 每個漢字及符號也是以兩個字節來表示。
  • 高字節從81~FE,低字節從40~FE。

GB18030

  • GB 18030,全稱《信息技術 中文編碼字符集》,與GB2312、GBK編碼兼容,可支持27484個文字
  • 採用變長多字節編碼,每個字可以由1個、2個或4個字節組成。
  • 1字節從00~7F; 2字節高字節從81~FE,低字節從40到7E和80到FE;4字節第一三字節從81~FE,第二四字節從30~39。

Unicode 字符集

Unicode是國際組織制定的可以容納世界上所有文字和符號的字符編碼方案。UNICODE字符集有多種編碼方式,分別是UTF-8,UTF-16和UTF-32。

UTF-8

  • 是針對Unicode的一種可變長度字符編碼。
  • 它可以用來表示Unicode標準中的任何字符,而且其編碼中的第一個字節仍與ASCII相容,使得原來處理ASCII字符的軟件無須或只進行少部份修改後,便可繼續使用。
  • UTF-8使用1~4字節爲每個字符編碼(ASCIl字符只需1字節編碼, 拉丁文、希臘文等需要兩個字節編碼, 中日韓文字使用三字節編碼, 其他極少使用的語言字符使用4字節編碼號)

UTF-16

  • 把Unicode字符集的抽象碼位映射爲16位長的整數(即碼元)的序列,用於數據存儲或傳遞。
  • UTF-16比起UTF-8,好處在於大部分字符都以固定長度的字節 (2字節) 儲存,但UTF-16卻無法兼容於ASCII編碼。

UTF-32

  • 一種將Unicode字符編碼的協定,對每一個Unicode碼位使用恰好32位元,其它的 Unicode 編碼方式則使用不定長度編碼。
  • 採用4字節編碼,處理速度比較快,但是浪費空間,傳輸速度慢。

一個例子理解編碼解碼的廬山面目

我們敲代碼的程序員,接觸最多的就是“hello word”。計算機只認識0和1,它是怎麼展示hello word的呢?

上一小節,我們已經知道編碼、字符集的知識。我們可以用ASCII編碼,把“hello word”翻譯成計算機認識的0、1。有興趣的朋友可以去查一下 ASCII對照表

寫代碼總是莫名其妙的亂碼?這一篇教你從原理出發徹底幹掉它

 

計算機存儲的是hello world的0、1二進制碼,先將二進制碼解碼成對應的字符,然後在屏幕上渲染出來,我們看到的就是hello world了

寫代碼總是莫名其妙的亂碼?這一篇教你從原理出發徹底幹掉它

 

亂碼如何產生的呢?

亂碼產生的原因主要有兩個,一是文本字符編碼過程與解碼過程使用了不同的編碼方式,二是使用了缺少某種字體庫的字符集引起的亂碼

編碼與解碼使用了不同的編碼方式

寫代碼總是莫名其妙的亂碼?這一篇教你從原理出發徹底幹掉它

 

例子中,用了utf-8編碼,使用了GBK解碼,結果產生了亂碼。因爲在utf-8中,一個漢字用三個字節編碼,而GBK中,每個漢字用兩個字節表示,所以產生了亂碼。

使用了缺少某種字體庫的字符集

寫代碼總是莫名其妙的亂碼?這一篇教你從原理出發徹底幹掉它

 

我們知道GB2312是不支持繁體字的,所以使用缺少某種字體庫的字符集編碼,會產生亂碼。

亂碼又如何解決呢

使用支持要展示字體的字符集編碼,並且編解碼使用同一種編碼方式,就可以解決亂碼問題了。

接下來列舉一下亂碼的經典場景與解決方案

IntelliJ Idea亂碼問題

IDE項目中的中文亂碼問題?File->settings->Editor->File Encodings,設置一下編碼方式utf-8

寫代碼總是莫名其妙的亂碼?這一篇教你從原理出發徹底幹掉它

 

IDE控制檯中文亂碼?嘗試一下這種方式,打開IDE安裝目錄,找到

寫代碼總是莫名其妙的亂碼?這一篇教你從原理出發徹底幹掉它

 

在文本末尾添加-Dfile.encoding=UTF-8

 

寫代碼總是莫名其妙的亂碼?這一篇教你從原理出發徹底幹掉它

數據庫亂碼問題

查看數據庫編碼:

show variables like 'character_set%'
複製代碼

寫代碼總是莫名其妙的亂碼?這一篇教你從原理出發徹底幹掉它

 

設置session、global範圍的編碼方式

//session 範圍
set character_set_server=utf8;
set character_set_database=utf8;
//global 範圍
set global character_set_database=utf8;
set global character_set_server=utf8;
複製代碼

session、global範圍編碼,重啓mysql可能編碼又變回去了,可以嘗試另外一種方式。在mysql(windows環境)的my.ini配置文件中修改或添加下列內容

[mysql]
default-character-set=utf8 
[mysqld]
default-character-set=utf8 
[client]
default-character-set=utf8
複製代碼

編碼角度的亂碼問題

寫代碼的時候出現中文亂碼?追蹤定位到編碼解碼的地方,設置用同一種編碼方式。

寫代碼總是莫名其妙的亂碼?這一篇教你從原理出發徹底幹掉它

 

寫在最後

需更加深入學習java知識的小夥伴以及即將參加面試的小夥伴可以關注我後私信“學習”免費獲取以下資料哦!

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