在閱讀代碼的時候,經??吹接龅竭@樣一句
$this->pdo->exec("SET NAMES '" . $config["charset"] . "'");
心生疑惑 ,“為什么在數據讀寫之前,要寫這樣一句”。
求助了一下搜索引擎,原來是為了避免亂碼的出現。下面就來說說 MySQL 為什么會出現亂碼以及解決亂碼的方法
先明確幾個概念
什么是字符集(character set)呢?mysql 的文檔給了一個很容易理解的例子
假設有個包含4個字符的字母表 A,B,a,b。
給每個字母編一個號,A=0,B=1,a=2,b=3
這里的 A 就是 符號(symbol),0 就是 A 的編碼(encoding)
符號和符號對應的編碼就組成了一個字符集
那么什么又是 collation 呢?
簡單來說,它就是 order by
時,用來排序的規則。
字符集 utf8 下常用的 collation 有 utf8_general_ci、utf8__bin。
ci 指的就是case incensitive
,大小寫不敏感。比較時,a 和 A 是相同的
為什么會出現亂碼?
MySQL 的中的字符集變量
- character_set_server:默認的內部操作字符集
- character_set_client:客戶端來源數據使用的字符集
- character_set_connection:連接層字符集
- character_set_results:查詢結果字符集
- character_set_database:當前選中數據庫的默認字符集
- character_set_system:系統元數據(字段名等)字符集
- 還有以collation_開頭的同上面對應的變量,用來描述字符序
客戶端要讀寫數據庫時,需要建立一條數據庫連接 connection。
數據從客戶端到服務端
數據在 connection 上,傳輸的過程中需要經過編碼轉換
character_set_client ---> character_set_connection ---> column_character_set
|--> table_character_set
|--> database_character_set
|--> character_set_server
|--> character_set_result
- 服務端收到客戶端的數據時,認為客戶端的數據編碼為
character_ser_client
- 隨后將從客戶端接收到的數據,轉換為
character_set_connection
編碼 - 在需要進行數據庫內部操作(寫到表等)的時候,將
character_set_connection
編碼轉換字段、表、數據庫、服務器對應的編碼類型 - 若以上值不存在,則轉化為
character_set_results
數據從服務端到客戶端
character_set_result ---> character_set_connection ---> character_set_client
為數據從客戶端到服務端相反過程
可以看到轉換過程中,有幾個地方可能會產生亂碼
- character_set_client 和真實的客戶端編碼不同
- client->server, server->client 兩個過程中使用的編碼不一致
- 編碼轉換不可逆,導致信息丟失
這里有解決辦法
在進行連接后,就進行設置編碼,之后的讀寫都不再改變
SET NAMES character_name
相當于
SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET character_set_connection = charset_name;
這樣可以解決上述的前連個問題
但是第三個問題,要看數轉換的數據是否存在兩個字符集之間的轉換不可逆的字符
如有錯誤,還望指正!