MySQL字符集中文亂碼解析
問題描述
假設(shè)有三個表test_gbk
,test_utf8
,test_latin1
,創(chuàng)建的時候字符集分別為gbk
,utf8
,latin1
。表結(jié)構(gòu)為
Field | Type | Null | Key | Default | Extra |
---|---|---|---|---|---|
name | varchar(512) | YES | NULL |
"中"字的gbk
十六進(jìn)制表示為:0xd6 d0
;utf8 16
進(jìn)制表示為:0xe4 b8 ad
問題1
執(zhí)行下列語句:
set names 'latin1'; insert into test_latin1 values( '中'); //此處'中'為gbk格式 select name from test_latin1;
結(jié)果是亂碼,還是正常顯示?
問題2
執(zhí)行下列語句:
set names 'gbk'; insert into test_latin1 values( '中'); //此處'中'為gbk格式 select name from test_latin1;
結(jié)果是亂碼,還是正常顯示?
問題3
執(zhí)行下列語句:
set names 'latin1'; insert into test_utf8 values( '中'); //此處'中'為gbk格式 select name from test_utf8;
結(jié)果是亂碼,還是正常顯示?
原理篇
字符集介紹
為了解釋上述問題,首先要了解字符集為何物。字符集也叫字符編碼,就是將字符集合一一映射成一個數(shù)。以下簡單介紹一下幾種字符集:
基礎(chǔ)ASCII
編碼:
0x00-0x7F
表示所有的大寫和小寫字母,數(shù)字0 到9、標(biāo)點(diǎn)符號, 以及在美式英語中使用的特殊控制字符。
latin1
編碼:
單字節(jié)編碼,編碼范圍是0x00-0xFF
,0x00-0x7F
,和ASCII
保持一致,0x80-0x9F
之間是控制字符,0xA0-0xFF
之間是文字符號。
gbk
編碼:
使用一字節(jié)和雙字節(jié)編碼,0x00–0x7F
范圍內(nèi)是一位,和ASCII保持一致。雙字節(jié)的第一字節(jié)范圍是0x81-0xFE
(不含0x80
和0xFF
)。
utf8
編碼:
使用一至四字節(jié)編碼,0x00–0x7F
范圍內(nèi)是一位,和ASCII保持一致。其它字符用二至四個字節(jié)變長表示。
字符集編碼轉(zhuǎn)換舉例:
0xB1(latin-1)
->'±'
-> 0xC2 B1 (utf8)
兩個重要的點(diǎn)
0x00-0x7F
區(qū)間,上述字符集是一致的,也就是說英文字符無需轉(zhuǎn)碼。不同編碼,字符集合不完全一樣,存在某字符集的字符無法映射到另外一個字符集。
比如gbk
編碼中的中文字符,轉(zhuǎn)成latin-1
編碼時,就找不到對應(yīng)的二進(jìn)制編碼。MySQL做字符集轉(zhuǎn)換的時候,gbk
中文字符->latin-1,很多就轉(zhuǎn)成'?'號(0x3f
),這種大集合轉(zhuǎn)成小集合,基本是不可逆的。
MySQL執(zhí)行過程
對一個MySQL的執(zhí)行過程,字符集轉(zhuǎn)換,一般涉及到一下三個步驟:
收到請求,將請求數(shù)據(jù)從
character_set_client
->character_set_connection
。內(nèi)部操作,將數(shù)據(jù)從
character_set_connection
-> 表創(chuàng)建的字符集。結(jié)果輸出,將數(shù)據(jù)從表創(chuàng)建的字符集 ->
character_set_results
。
當(dāng)執(zhí)行set names "charset"
; 相當(dāng)于把character_set_client
, character_set_connection
,character_set_results
統(tǒng)一設(shè)置為"charset"
。
終端顯示字符集
此外如果你用securecrt終端來顯示的話,如果不想亂碼的話,appearance
->character encoding
也需要設(shè)置成正確的字符集。
問題詳解
問題1
執(zhí)行下列語句:
set names 'latin1'; insert into test_latin1 values( '中'); //此處'中'為gbk格式 select name from test_latin1;
結(jié)果是亂碼,還是正常顯示?
答:結(jié)果是正常顯示。
執(zhí)行流程如下:
set names 'latin1'
;相當(dāng)于把character_set_client
,character_set_connection
,character_set_results
統(tǒng)一設(shè)置為'latin1'
。Character_set_client
告訴MySQL Server
,傳入的是一個latin1編碼的,也就是單字節(jié)流,'中'這個輸入,其實(shí)當(dāng)作了0xD6 D0傳入。因?yàn)?code>character_set_client ->
character_set_connection
->table charset
->character_set_results
為latin1
->latin1
->latin1
->latin1
, 編碼完全一致,數(shù)據(jù)沒有做任何轉(zhuǎn)換,所以輸入是0xD6 D0,最后的輸出也還原為0xD6 D0。如果你的securecrt的顯示字符集設(shè)置為
gbk
,那么最后的輸出0xD6 D0
就會顯示成'中'。
問題2
執(zhí)行下列語句:
set names 'gbk'; insert into test_latin1 values( '中'); //此處'中'為gbk格式 select name from test_latin1;
結(jié)果是亂碼,還是正常顯示?
答:結(jié)果是亂碼。
執(zhí)行流程如下:
set names 'gbk'
;相當(dāng)于把character_set_client
,character_set_connection
,character_set_results
統(tǒng)一設(shè)置為'gbk'
。Character_set_client
告訴MySQL Server
,傳入的是一個gbk編碼的,'中'這個輸入,當(dāng)作了0xD6 D0
傳入。因?yàn)?code>character_set_client ->
character_set_connection
->table charset
->character_set_results
為gbk
->gbk
->latin1
->gbk
, 其中gbk
->latin1
的時候,因?yàn)?#39;中'這個字符在latin1字符集里找不到,就會轉(zhuǎn)換成'?'號(0x3F
),然后latin1
->gbk
,'?'號在gbk
字符集里面也是0x3F
,最后輸出就是0x3F
,即'?'號。
問題3
執(zhí)行下列語句:
set names 'latin1'; insert into test_utf8 values( '中'); //此處'中'為gbk格式 select name from test_utf8;
結(jié)果是亂碼,還是正常顯示?
答:正常顯示。
執(zhí)行流程如下:
set names 'latin1'
;相當(dāng)于把character_set_client
,character_set_connection
,character_set_results
統(tǒng)一設(shè)置為'latin1'
。Character_set_client
告訴MySQL Server
,傳入的是一個latin1
編碼的,'中'這個輸入,當(dāng)作了0xD6 D0
傳入。因?yàn)?code>character_set_client ->
character_set_connection
->table charset
->character_set_results
為latin1
->latin1
->utf8
->latin1
, 其中latin1
->utf8
的時候,輸入'中' (0xD6 D0)會當(dāng)作兩個字符進(jìn)行utf8轉(zhuǎn)換,轉(zhuǎn)換為0xC3 96 C3 90
,然后utf8
->latin1
的時候,會把0xC3 96
轉(zhuǎn)換成0xD6
,0xC3 90
轉(zhuǎn)成0x D0
,最后輸出0xD6 D0
。負(fù)負(fù)得正,之所以數(shù)據(jù)沒有失真的原因是因?yàn)樾〖贤蠹限D(zhuǎn),再轉(zhuǎn)回來,操作可逆。如果你的securecrt的顯示字符集設(shè)置為gbk,那么最后的輸出
0xD6 D0
就會顯示成'中'。
終極解決方案
從上面的問題執(zhí)行流程來看,有沒有終極解決方案呢?其實(shí)很簡單,表創(chuàng)建的字符集和set names
都設(shè)置成同一個字符集,就基本可以滿足輸入數(shù)據(jù)不會在轉(zhuǎn)換過程中失真,也就是說輸入是什么,輸出就是什么。建議有中文的都設(shè)置成utf8字符集,一勞永逸。
以上就是MySQL字符集中文亂碼解析的詳細(xì)內(nèi)容,更多關(guān)于MySQL字符集中文亂碼的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mysql數(shù)據(jù)庫實(shí)現(xiàn)超鍵、候選鍵、主鍵與外鍵的使用
數(shù)據(jù)庫設(shè)計(jì)時,關(guān)鍵字的概念至關(guān)重要,本文就來介紹一下mysql數(shù)據(jù)庫實(shí)現(xiàn)超鍵、候選鍵、主鍵與外鍵的使用,具有一定的參考價值,感興趣的可以了解一下2024-09-09Windows MySQL修改配置文件my.ini不生效問題
在Windows Server 2019上修改MySQL 5.6的安裝目錄下my.ini文件后,需要通過修改注冊表中的ImagePath值來確保MySQL讀取新的配置文件,修改時應(yīng)確保配置文件路徑正確,并且新配置不會覆蓋原有配置,以保證修改生效2025-01-01關(guān)于mysql數(shù)據(jù)庫格式化簡單介紹
本文將介紹關(guān)于mysql數(shù)據(jù)庫格式化時需要注意的一些問題,需要的朋友可以參考下2012-11-11Windows環(huán)境下的MYSQL5.7配置文件定位圖文分析
本文通過圖文并茂的形式給大家介紹了Windows環(huán)境下的MYSQL5.7配置文件定位 ,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-05-05MySQL數(shù)據(jù)庫SELECT查詢表達(dá)式解析
這篇文章主要介紹了MySQL數(shù)據(jù)庫SELECT查詢表達(dá)式解析,文中給大家介紹了select_expr 查詢表達(dá)式書寫方法,需要的朋友可以參考下2018-04-04