爲何編碼不一樣會出現亂碼?

簡介:本章節經過情景模擬,和你們一塊兒探討爲何編碼不一樣會出現亂碼html

做者:手辨

實爲吾之愚見,望諸君酌之!聞過則喜,與君共勉

第一章  情景模擬
第一節 工具準備

準備一些須要的工具,方便後面的虛擬場景mysql

1.1.1 模擬密碼本A

密碼本A的字符座標規則:假設一個字符的密文由三部分組成,順序分別是"U+5600的56"+"表格最上方的座標"+"表格最左邊的座標",舉例"570B"是"國","56E7"是"囧",以下第一個和第二個密碼錶,這些密文是16進制的格式,下同sql




1.1.2 模擬密碼本B

密碼本B的字符座標規則:假設一個字符的密文由三部分組成,順序分別是"表格左上角的字符串"+"表格最左邊的座標"+"表格最上方的座標",舉例"87E5"是"囧","87F8"是"國",以下第一個密碼錶,這些密文也是16進制的格式,下同



數據庫

1.1.3 密文的加密規則

即咱們可能還須要對咱們拿到的密文進行再一次的加密,好比」囧」的密文是」 "56E7」按照開頭假設的密文規則,這是16進制的數,那咱們還須要把這個16進制的56E7經過必定的規則,再次加密,針對這兩個密碼本的加密規則假設以下:安全

密碼本A密文再次加密規則:session

16進制密文轉換爲10進制後對應的範圍工具

16進制數轉換爲2進制後對應的加密格式測試

0至127編碼

0xxxxxxx加密

128至2047

110xxxxx 10xxxxxx

2048至65535

1110xxxx 10xxxxxx 10xxxxxx

65536至2097151

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

好比56E7這個密文再次加密,他轉換成10進制是22247,22247在」2048-65535」的範圍內,那他的加密格式就是1110xxxx10xxxxxx10xxxxxx,暫且稱它爲466格式,如何加密呢?以下:

  • 先把56E7轉換成二進制爲:0101011011100111
  • 按照1110xxxx 10xxxxxx 10xxxxxx的格式,對56E7轉換成的二進制0101011011100111進行拆分:

       首先,0101011011100111--------按照4 6 6的格式拆分後--------->0101 011011 100111

       而後,對0101 011011 100111按照466格式進行轉換------->11100101 10011011 10100111

       把轉換後的111001011001101110100111轉換爲16進制就是e59ba7,e59ba7就是56E7再次加密的最終密文,這樣就由原來的"56E7"是"囧"變成" E59BA7"是"囧",甚至能夠根據這個加密方式,生成一個新的密碼本C

密碼本B的再次加密規則:

密碼本B的16進制密文再也不進行二次加密,直接使用得到的16進制密文,例如56E7不會與密碼本A同樣再次加密,直接使用56E7

第二節 漫畫情景

1.2.1 情景故事

模擬一個虛構的場景,場景裏的元素是:

  • 遠在外地的小明
  • 與小明同部門的小麗
  • 安所有門的同窗
  • 與小明同部門的小紅
  • 一個信息安全很是嚴格的公司
  • 加密的檔案管理

今天,小明須要將一個很重要的數據,告訴公司的小麗把這個數據存到小明本身的檔案裏:


小明發現,他獲取到的數據是「闃塊噷」,而不是3天前存儲的」阿里」,若是小明意識到本身的錯誤,把小紅寄給他的數據先轉換成密碼本B的密文,再用轉換的密文對照密碼本A查找呢?

第二章 Mysql的亂碼

若是上面的場景,放到數據庫裏,會同樣嗎?拿mysql做爲例子,測試一下,數據從客戶端發送到server並存入表中成爲數據而後查詢該數據的過程,mysql中控制這個過程的編碼的參數主要是character_set_client,character_set_connection,character_set_results,表字符集以及程序字符集等,能夠看下文檔

第一節      mysql的情景對應

字符集:

unicode  gbk

字符編碼規則:

utf8:只針對unicode,至關於第一章情景中的二次加密

gbk:不對gbk再編碼

參數與情景對應:

程序字符集:小明加密數據時使用的密碼本A

character_set_client:小明在寄信時,寫錯了數據加密方式,寫的是密碼本B,而不是A

character_set_connection:安全規則,須要轉換的加密格式

character_set_results:寄信時須要使用的加密格式

表或者庫字符集:小明的檔案的加密格式

Server端處理:小麗/小紅

第二節      mysql測試

2.2.1 第一次測試

參數設置:

程序字符集:utf8

character_set_client:utf8

character_set_connection:utf8

character_set_results:utf8

表或者庫字符集:utf8

Server端處理:mysql program 

測試結果:無亂碼

2.2.2 第二次測試

參數設置:

程序字符集:utf8

character_set_client:gbk

character_set_connection:utf8

character_set_results:utf8

表或者庫字符集:utf8

Server端處理:mysql program 

測試結果:只設置set character_set_client=gbk後,查詢亂碼

2.2.3 亂碼猜猜猜

結合第一章的場景,爲何亂碼呢?

2.2.3.1 第一步處理

程序將"阿里"以utf8編碼進行轉換髮送(unicode轉utf8與情景中的規則一致),可是不當心在session中設置了character_set_client=gbk,character_set_connection =utf8,character_set_results=utf8,相似以下現象:

形狀:阿里

Utf8方式下的16進制編碼值:E998BF E9878C                   

二進制:11101001 10011000 10111111 11101001 10000111 10001100(utf8將2個漢字轉換後6個字節)

2.2.3.2 第二步處理

到達server端,server經過character_set_client=gbk知道客戶端發過來的數據是gbk編碼的(實際是utf8編碼的,客戶端不當心寫錯了),那server端就按照gbk來解碼了,解碼後以下:

二進制:11101001 10011000 10111111 11101001 10000111 10001100(程序端的兩個漢字通過utf8轉換後的二進制數據,一共6字節,可是server端不知道,由於server經過character_set_client=gbk得知,這不是utf8,這是gbk的,我要按照gbk來解碼)

gbk轉換後的16進制編碼值:E998 BFE9 878C(經過這個編碼,查找gbk的碼錶,獲取他的形狀爲:闃塊噷)

2.2.3.3 第三步處理

Servre還須要檢查character_set_connection參數,發現character_set_connection=utf8,而以前是gbk,那就須要將gbk的這三個字節轉換爲utf8,怎麼轉換呢?按照第一章的情景,先轉unicode(密碼本A),再將獲得的unicode再次編碼爲utf8:

「闃塊噷「三個字符的unicode編碼是 95c3 5757 5677

轉換成二進制:10010101 11000011 01010111 01010111 01010110 01110111(這三個漢字是6個字節)

經過unicode,再轉換爲utf8的實現方式,實現方式以下:

首先轉換95c3( 1001010111000011),他的範圍在第三行(3字節)

1001 010111 000011,根據第三行的格式,從低位往高位的順序,按照格式分紅4 6 6 的格式

按照格式填充,填充後:11101001 10010111 10000011,填充後轉換成16進制是e99783,則」闃」在utf8中就是」e99783」,一樣,」塊」在utf8中就是」 e59d97」,」 噷」在utf8中就是」 e599b7」, 因而轉換後的結果就是:

編碼:E99783 E59D97  E599B7

二進制:111010011001011110000011111001011001110110010111111010000000000000000000

2.2.3.4 第四步處理                          

server端處理好以後,繼續向下走,準備存儲數據了,檢查了存儲的表是utf8的,不須要轉換了,直接存儲

編碼:E99783 E59D97  E599B7

二進制:111010011001011110000011111001011001110110010111111010000000000000000000

2.2.3.5 查詢返回

程序插入完成後,想查詢下以前插入的數據,因而客戶端在當前session下執行了select操做,server端收到請求後,去表裏找數據,找到了以前寫入的數據

111010011001011110000011111001011001110110010111111010000000000000000000

server檢查character_set_results=utf8,與表的字符編碼是一致的,那就不須要轉換了,直接把111010011001011110000011111001011001110110010111111010000000000000000000給程序了,程序收到後,就解碼111010011001011110000011111001011001110110010111111010000000000000000000,由於程序自己也是utf8的,不須要轉換,因而按照utf8來解碼數據,111010011001011110000011111001011001110110010111111010000000000000000000的16進制是E99783E59D97E599B7(符號是闃塊噷,亂碼了)

相關文章
相關標籤/搜索