mysql編碼問題——charset=utf8你真的弄明白了嗎?

導讀:以前學習mysql的時候,一直不知道“charset=utf8”是什麼意思,更不知道“set names gbk”是什麼意思,通過這篇文章將會給大家詳細介紹客戶端字符集、聯接器connection、MySQL server字符集的設置。
  如果覺得文章寫得好,如果你想要博客文章中的數據,請關注公衆號:【數據分析與統計學之美】,添加作者【個人微信】,進羣和作者交流!

目錄
  1、一個建表語句引出的問題
  2、查看當前電腦使用的字符集
  3、你發現這個問題了嗎?
  4、你不熟悉的幾個命令
   1)連接器connection的作用與工作流程(文字敘述)
     ① 連接器的作用
     ② 連接器的工作流程
   2)圖示法講解connection的作用與工作流程
     ① 第一種方式
     ② 第二種方式
  5、上述兩種圖示法的實際操作演示
   1)先了解如下幾個代碼
   2)代碼演示過程如下
  6、產生亂碼的兩個原因
   1)編碼和解碼不一致導致的亂碼
   2)傳輸過程中,丟失字節導致的亂碼
  7、對實際情況的分析(什麼都不設置,系統默認是如何呢?)
   1)MySQL系統參數如下
   2)set names gbk的含義

1、一個建表語句引出的問題

create table student(
    sid int primary key aotu_increment,
    sname varchar(20) not null,
    age int
)charset=utf8;

思考一個問題:
  對於剛剛安裝好的MySQL,我們隨意寫了一個建表語句。當建表時指定charset=utf8的時候,此時,插入中文爲什麼又可以插入中文,並且不亂碼呢?當我們建表時,不指定 charset=utf8的時候,此時,插入中文,爲什麼會報錯呢?

2、查看當前電腦使用的字符集

在這裏插入圖片描述
  打開電腦黑窗口(CMD),接着點擊鼠標右鍵,然後選擇屬性,並查看“選項”這一欄。通過上圖可以知道:CMD中輸入文字使用的字符編碼是GBK。

3、你發現這個問題了嗎?

在這裏插入圖片描述
問題如下:
  客戶端client輸入的字符,都是採用GBK編碼的。mysql服務器存儲的字符又是UTF8編碼的。
  那麼,我們向數據庫中插入數據,從數據庫中查找數據,返回到界面中,要想保證字符不亂碼,肯定是經過了"編碼轉換過程的"。我要問的是,究竟是什麼東西完成了這個編碼的轉換過程的?
  

4、你不熟悉的幾個命令

-- 查看數據庫支持的所有的字符集(這句命令自己下去操作)。
mysql> show character set;
-- 查看系統當前狀態,裏面可以看到部分字符集設置。
mysql> status;
-- 查看系統字符集設置,包括所有的字符集設置
mysql> show variables like '%char%';

操作結果如下:
在這裏插入圖片描述
  通過上圖我們可以看到有一個東西,叫做"connection",中文名叫做"連接器"。"連接器"就是3中那個問題,我們想要知道的答案。也就是說:這個轉換過程依賴的就是這個connection。

1)連接器connection的作用與工作流程(文字敘述)
① 連接器的作用

連接客戶端與服務端,進行字符集的轉換。連接器有這種自動轉換的功能。

② 連接器的工作流程

Ⅰ 客戶端的字符先發給連接器,連接器選擇一種編碼將其轉換(轉換之後的編碼,與連接器的編碼格式一致),進行臨時存儲。
Ⅱ 接着,連接器再次轉換成服務器需要的編碼,並最終存儲在服務器中。
Ⅲ 然後,服務器返回的結果,再次先通過連接器,連接器將其轉化爲與客戶端一致的字符集,就可以在客戶端正常顯示了。

2)圖示法講解connection的作用與工作流程
① 第一種方式

在這裏插入圖片描述
圖示說明:
  我們已經知道:在CMD窗口中輸入的字符,採用的字符集是GBK,也就是說客戶端(client)的字符集是GBK。而寫入到數據庫中數據採用什麼格式寫入,我們在建表的時候已經指明瞭"charset=utf8",也就是說,mysql服務器(server)的字符集是UTF8。此時,假如說連接器(connection)的字符集是UTF8,這個寫入數據庫的過程是怎麼進行的呢,下面我們進行文字說明。
  首先,在客戶端輸入的字符,使用的字符集是GBK。當經過連接器的時候,連接器會進行"字符集的自動轉換",將原來的子符(以GBK進行編碼)轉換爲以UTF8格式的編碼字符,臨時存儲在連接器中。
  接着,連接器發現mysql服務器使用的字符集,與自身字符集完全一致,都是UTF8。於是,直接發給mysql服務器,進行最終的存儲。

  “當我們從mysql服務器查數據的時候,返回過程又是怎麼進行的呢?”
  首先 ,mysql服務器會將結果以UTF8編碼格式進行返回,通過連接器的時候,連接器發現mysql服務器的字符集,與自身字符集一致,於是順利通過連接器。當連接器準備將結果發送給客戶端的時候,發現客戶端要求返回的字符集是GBK。因此,連接器會進行"字符集的自動轉換",將返回的結果(以UTF8進行編碼)轉換爲以GBK格式的編碼,進行顯示,並最終發送給客戶端,顯示在CMD窗口中。

② 第二種方式

在這裏插入圖片描述
  假如說連接器(connection)的字符集是GBk,這個過程又該是怎麼進行的呢,下面我們仍然進行文字說明。
  首先,在客戶端輸入的字符,使用的字符集是GBK。當經過連接器的時候,連接器發現客戶端發送過來的字符的字符集,與自身字符集相同,因此順利通過了連接器。
  接着,當字符通過連接器發送給mysql服務器進行存儲的時候,發現mysql服務器的字符集是UTF8,與自身的字符集GBK並不一致。因此,連接器此時又會進行"字符集的自動轉換",將該字符(以GBK進行編碼)轉換爲以UTF8格式的編碼,進行顯示,轉換完成以後,再次發送給mysql服務器,進行最終的存儲。

  “當我們從mysql服務器查數據的時候,返回過程又是怎麼進行的呢?”
  首先 ,mysql服務器會將結果以UTF8編碼格式進行返回,通過連接器的時候,連接器發現mysql服務器的字符集,與自身的字符集並不一致,於是連接器會進行"字符集的自動轉換",將返回的結果(以UTF8進行編碼)轉換爲以GBK格式的編碼,進行顯示。接着連接器又將轉換後的結果,準備發送給客戶端,此時發現客戶端的字符集,與自身的字符集一致。因此直接發送給客戶端,成功在CMD窗口中顯示。

5、上述兩種圖示法的實際操作演示

1)先了解如下幾個代碼
1)設置客戶端的字符集
set character_set_client=gbk;
2)設置連接器的字符集
set character_set_connection=utf8;
3)設置返回結果的字符集
set character_set_results=gbk;
2)代碼演示過程如下
mysql> desc student;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| sid   | int(11)     | NO   | PRI | NULL    | auto_increment |
| sname | varchar(20) | NO   |     | NULL    |                |
| age   | int(11)     | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

mysql> select * from student;
+-----+-------+------+
| sid | sname | age  |
+-----+-------+------+
|   1 | 張三  |   23 |
|   2 | 李四  |   25 |
|   3 | 王五  |   43 |
|   4 | 趙六  |   34 |
+-----+-------+------+
4 rows in set (0.00 sec)

mysql> #要想不亂碼,需要指定客戶端的編碼,讓連接器不理解錯誤。
mysql> #這樣就不會存入錯誤數據。
mysql> #往回取數據的時候,還要告訴連接器,如果你從服務器返回,你應該給我轉成什麼格式。
mysql> #因此,一共需要設置3個參數:
mysql> #1、客戶端發送的編碼;
mysql> #2、連接器使用的編碼;
mysql> #3、返回數據的編碼;
mysql> 
mysql> 
mysql> 
mysql> #當前的情況是:客戶端是GBK,服務器最終存儲的是UTF8。
mysql> #因此,你就要明確告訴服務器,我的客戶端是GBK的。
mysql> #命令如下:
mysql> set character_set_client=gbk;
Query OK, 0 rows affected (0.00 sec)

mysql> #再告訴連接器,使用UTF8。
mysql> #命令如下:
mysql> set character_set_connection=utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> #再告訴,如果你返回值給我看的話,也請返回GBK。
mysql> #命令如下:
mysql> set character_set_results=gbk;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into student(sname,age) values ("編碼不亂",22);
Query OK, 1 row affected (0.10 sec)

mysql> select * from student;
+-----+----------+------+
| sid | sname    | age  |
+-----+----------+------+
|   1 | 張三     |   23 |
|   2 | 李四     |   25 |
|   3 | 王五     |   43 |
|   4 | 趙六     |   34 |
|   6 | 編碼不亂 |   22 |
+-----+----------+------+
5 rows in set (0.00 sec)

mysql> #我的客戶端是GBK,但是我偏要騙對方,是UTF8。
mysql> set character_set_client=utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into student(sname,age) values ("編碼亂不亂?",22);
ERROR 1366 (HY000): Incorrect string value: '\xB1\xE0\xC2\xEB\xC2\xD2...' for column 'sname' at row 1
mysql> #此時,"編碼亂不亂?"對應的GBK的內碼,轉換成UTF8,壓根就出錯,不允許插入。
mysql> set character_set_client=gbk;
Query OK, 0 rows affected (0.00 sec)

mysql> #我偏要對方返回給我的數據是UTF8。
mysql> set character_set_results=utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from student;
+-----+--------------+------+
| sid | sname        | age  |
+-----+--------------+------+
|   1 | 寮犱笁       |   23 |
|   2 | 鏉庡洓       |   25 |
|   3 | 鐜嬩簲       |   43 |
|   4 | 璧靛叚       |   34 |
|   6 | 緙栫爜涓嶄貢 |   22 |
+-----+--------------+------+
5 rows in set (0.00 sec)

mysql> #此時,請問我再插入數據。
mysql> insert into student(sname,age) values ("編碼亂不亂?",22);
Query OK, 1 row affected (0.09 sec)

mysql> select * from student;
+-----+--------------------+------+
| sid | sname              | age  |
+-----+--------------------+------+
|   1 | 寮犱笁             |   23 |
|   2 | 鏉庡洓             |   25 |
|   3 | 鐜嬩簲             |   43 |
|   4 | 璧靛叚             |   34 |
|   6 | 緙栫爜涓嶄貢       |   22 |
|   7 | 緙栫爜涔變笉涔憋紵 |   22 |
+-----+--------------------+------+
6 rows in set (0.00 sec)

mysql> #雖然插入返回的數據仍然是亂碼,但是屬於的是,編碼和解碼不一致導致的亂碼,可以修復。
mysql> set character_set_results=gbk;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from student;
+-----+--------------+------+
| sid | sname        | age  |
+-----+--------------+------+
|   1 | 張三         |   23 |
|   2 | 李四         |   25 |
|   3 | 王五         |   43 |
|   4 | 趙六         |   34 |
|   6 | 編碼不亂     |   22 |
|   7 | 編碼亂不亂? |   22 |
+-----+--------------+------+
6 rows in set (0.00 sec)

mysql> #再看另外一種情況:
mysql> #聲明客戶端是GBK;
mysql> set character_set_client=gbk;
Query OK, 0 rows affected (0.00 sec)

mysql> #聲明連接器是GBK;
mysql> set character_set_connection=gbk;
Query OK, 0 rows affected (0.00 sec)

mysql> #聲明返回值是GBK;
mysql> set character_set_results=gbk;
Query OK, 0 rows affected (0.00 sec)

mysql> #如上操作,插入中文,會不會亂碼?
mysql> insert into student(sname,age) values ("真不亂?",666);
Query OK, 1 row affected (0.07 sec)

mysql> select * from student;
+-----+--------------+------+
| sid | sname        | age  |
+-----+--------------+------+
|   1 | 張三         |   23 |
|   2 | 李四         |   25 |
|   3 | 王五         |   43 |
|   4 | 趙六         |   34 |
|   6 | 編碼不亂     |   22 |
|   7 | 編碼亂不亂? |   22 |
|   8 | 真不亂?     |  666 |
+-----+--------------+------+
7 rows in set (0.00 sec)

mysql> #現在,我偏偏聲明連接器的字符集是latin1。
mysql> set character_set_connection=latin1;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into student(sname,age) values ("還不亂?",888);
Query OK, 1 row affected, 1 warning (0.32 sec)

mysql> select * from student;
+-----+--------------+------+
| sid | sname        | age  |
+-----+--------------+------+
|   1 | 張三         |   23 |
|   2 | 李四         |   25 |
|   3 | 王五         |   43 |
|   4 | 趙六         |   34 |
|   6 | 編碼不亂     |   22 |
|   7 | 編碼亂不亂? |   22 |
|   8 | 真不亂?     |  666 |
|   9 | ????         |  888 |
+-----+--------------+------+
8 rows in set (0.00 sec)

mysql> #這次亂碼,是因爲latin1容量小,gbkt容量大。這個轉換就像大魚過小魚網一樣,丟了塊肉。
mysql> #也就是說,大容量轉換爲小容量時,會丟失字節。
mysql> #思考一下:這次亂碼能否修復?
mysql> #不能。。。
mysql> #因此,記住這個原則:
mysql> #字符集,服務器>=連接器>=客戶端。
mysql> #也即:進行字符集的轉換的時候,必須是小容量轉換爲大容量(小魚過大魚網)d,纔不會丟失字節。
mysql> #########################################
mysql> #再回頭看:一個比較巧的地方。
mysql> #client、connection、server字符集設置的都是GBK,不會導致亂碼。
mysql> #如果三者都是GBK,可以簡寫成如下形式:
mysql> #set names gbk;
mysql> #這一句話,其實表示了3句話的含義。
mysql> Terminal close -- exit!

6、產生亂碼的兩個原因

  • 解碼與實際編碼,不一致導致的亂碼,可修復。
  • 在傳輸過程中,由於編碼不一致,導致部分字節丟失,造成的亂碼,不可修復。
1)編碼和解碼不一致導致的亂碼

在這裏插入圖片描述

2)傳輸過程中,丟失字節導致的亂碼。

在這裏插入圖片描述

7、對實際情況的分析(什麼都不設置,系統默認是如何呢?)

1)MySQL系統參數如下

在這裏插入圖片描述
根據上圖可以知道:
   我們使用圖中的這個命令,可以查看系統所有的字符集的設置。從圖中可以清楚的看到,客戶端的字符集默認是gbk,連接器的字符集默認是gbk,返回值的字符集是gbk,mysql服務器的字符集默認是latin1。
  上述設置,是不是和圖2(如下所示)中的情況,非常相似。唯一不同的就是系統默認mysql服務器的字符集是latin1,而圖二中mysql服務器的字符集是utf8。
  “系統爲什麼將mysql服務器默認使用latin1字符集?你可以自行百度。”
  爲什麼要這麼設置呢?因爲latin1不支持中文,當我們插入中文的時候,當客戶端發送過去的字符,通過連接器,最後發送給mysql服務器的時候,連接器發現mysql服務器採用的字符級是latin1,字符集由gbk轉化爲latin1,就相當於大魚過小漁網一樣,一定會掉肉,而對於字符集來說就會丟失字節。丟失字節後存入的值,肯定也就是錯誤的,不正確的。
  由於mysql的檢測是很嚴格的,既然你存入的時候都會丟失字節,那麼存入的值肯定也是錯誤的,因此,我索性就不讓你插入。“這就是我們不設置mysql服務器字符集,想要插圖中文,提示1366錯誤的原因。”"ERROR 1366 (HY000): Incorrect string value: ‘\xD5\xC5\xC8\xFD’ ““for column ‘sname’ at row 1”
當我們使用"charset=utf8"命令,將mysql服務器的字符集設置爲utf8後,由於utf8是支持中文的,utf8是變長字符集,它能夠支持全世界所有國家的語言。因此,當你輸入一個以gbk格式編碼的中文,在utf8中肯定是也有自己的一套編碼格式,顯示同樣的文字(只不過此時是以utf8編碼的)。
  “最後用一個不那麼恰當的比喻,來說明字符集編碼。”
  拿一本新華字典(漢語字典),再拿一本牛津字典(英語字典)。此時,我們要查找一個同一個詞"中國”,在漢語字典中,"中國"採用的編碼是zhongguo,但是在牛津字典中,“中國"採用的編碼是china,你非要拿着zhongguo去牛津字典中查"中國”,你覺得你得到的結果是正常顯示,還是亂碼呢?
在這裏插入圖片描述

2)set names gbk的含義
當客戶端、連接器、返回值的字符集相同,並且都是gbk的時候,我們可以採取如下的簡寫方式:
set names gbk;
這就話其實包含了三層意思:
set character_set_client=gbk;
set character_set_connection=gbk;
set character_set_results=gbk;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章