mysql字符集之ems,navicat,PEAR::MDB2相關

關於mysql字符亂碼早就有標準的解決辦法,數據庫使用UTF8編碼,頁面也使用UTF8編碼,然後服務器端先作一次 'SET NAMES UTF8' 的查詢就萬事大吉。這次做一個項目對相關問題又有了深入一點的瞭解。

系統爲 Centos4.7,PHP 4.3.9, MYSQL 4.1

 

一、關於mysql數據庫編碼設定有兩種常見辦法

  1. 建立數據時採用默認的Latin1編碼,然後配置全部採用默認。這樣寫庫是什麼編碼取出來就是什麼編碼,就是說如果頁面是GB2312編碼那麼寫入就是經過Latin1編碼“pack”過的GB編碼,取出來之後也是GB2312編碼。這種類似RAW寫庫的方式不會產生亂碼,不過缺點也不少。比如在導入導出時可能會亂碼,缺乏兼容性如果數據庫升級很可能亂碼,貌似在搜索中文時會有兼容問題。
  2. 數據庫,前後臺全部採用UTF-8編碼。雖然三字節編碼相對浪費空間及流量但是從此大大減小不兼容性。早點時候會出現亂碼問題,現在大家都知道一條sql命令就可以解決。

先轉個網上搜索的

引用

此時,要是我們通過採用UTF-8的PHP程序從數據庫裏讀取數據,很有可能是一串“?????”或者是其他亂碼。網上查了半天,解決辦法倒是簡單,在連接數據庫之後,讀取數據之前,先執行一項查詢“SET NAMES UTF8”,即在PHP裏
mysql_query("SET NAMES UTF8");

即可顯示正常(只要數據庫裏信息的字符正常)。爲什麼會這樣?這句查詢“SET NAMES UTF8”到底是什麼作用?
到 MySQL命令行輸入“SET NAMES UTF8;”,然後執行“show variebles like“character_set_%”;”,發現原來爲latin1的那些變量“character_set_client”、 “character_set_connection”、“character_set_results”的值全部變爲utf8了,原來是這3個變量在搗蛋。查閱手冊,上面那句等於:

SET character_set_client = utf8;
SET character_set_results = utf8;
SET character_set_connection = utf8;


看看這3個變量的作用:
信息輸入路徑:client→connection→server;
信息輸出路徑:server→connection→results。
換句話說,每個路徑要經過3次改變字符集編碼。以出現亂碼的輸出爲例,server裏utf8的數據,傳入connection轉爲latin1,傳入 results轉爲latin1,utf-8頁面又把results轉過來。如果兩種字符集不兼容,比如latin1和utf8,轉化過程就爲不可逆的,破壞性的。所以就轉不回來了。
但這裏要聲明一點,“SET NAMES UTF8”作用只是臨時的,MySQL重啓後就恢復默認了。
接下來就說到MySQL在服務器上的配置問題了。豈不是我們每次對數據庫讀寫都得加上“SET NAMESUTF8”,以保證數據傳輸的編碼一致?能不能通過配置MySQL來達到那三個變量默認就爲我們要想的字符集 ?手冊上沒說,我在網上也沒找到答案。所以,從服務器配置的角度而言,是沒辦法省略掉那行代碼的。

 

先回答上面引用中末尾黑體字的問題: 在服務器端配置只能對控制檯命令行登錄起效果,而對PHP(應該也包括JAVA/ASP)連接無效

至於搜索到的修改mysql配置方法也存在問題

引用
修改默認字符集
(1) 最簡單的修改方法,就是修改mysql的my.ini(linux系統爲my.cnf)文件中的字符集鍵值,

在[client]下面加上
default-character-set = utf8
在[mysqld]下面加上
character_set_server = utf8
default-character-set = utf8
character-set-server = utf8
collation-server = latin1_swedish_ci
init_connect = 'SET collation_connection = utf8_general_ci'
init_connect = 'SET NAMES utf8'
修改完後,重啓mysql的服務,service mysql restart
 

我只要在my.cnf中[mysqld]小節裏面添加上面“character_set_server ”的設置就會導致重啓服務失敗,經過測試可行的配置如下:

[mysql]
default-character-set=utf8
[client]
default-character-set=utf8

 如果同時出現[mysql][client]對於default-character-set的定義,以最後出現的定義爲準。
並且此字符集定義隻影響控制檯登錄,sql管理軟件(如ems/navicat)不受影響需要自己設定
這個可以這樣驗證:在[mysql]中隨便輸入一個錯誤定義,然後service mysqld restart時並不會報錯,而是
在 #mysql -p 時報錯,比如:

mysql: unknown variable 'init_connect=SET collation_connection = utf8_bin'


注意如果是[client]中定義錯誤則服務將無法啓動。

 

如此配置可以對控制檯登錄(#mysql -p)到mysql命令行有效,所以運行 show variables like 'character%'; 這條命令可以得到如下信息:

 

mysql> show variables like  'collation_%';
+----------------------+-------------------+
| Variable_name        | Value             |
+----------------------+-------------------+
| collation_connection | utf8_general_ci   |	-> 由set name utf8決定
| collation_database   | latin1_swedish_ci |	-> 數據庫自身屬性
| collation_server     | latin1_swedish_ci |	-> mysql系統默認定義
+----------------------+-------------------+

 

對於collation_database  字段若沒有使用use命令選擇數據庫那麼其值取自下一項(collation_server )。

 

 

二、上面的設置只針對控制檯登錄,如果使用GUI軟件登錄那麼依據軟件自身對字符集的設定

  1. EMS MySQL Manager 2005 v3版本不支持UTF-8編碼,支持GB/GBK,2007 v4版本支持UTF-8。所以如果是採用Lation1建庫RAW寫庫的那麼v3版本使用默認數據庫字符集屬性就能正常顯示中文,v4新版得采用windows charset,中文版系統默認爲GBK,若DB爲GB2312也能顯示出中文 。如果採用UTF-8編碼建庫只有在v4新版本下中文才不亂碼。

    ems v4 for latin1
     

  2. navicat的設定比較怪。整個服務器採用統一編碼設定,如果其上的數據庫採用不同的編碼格式那麼相互之間就會亂碼。在圖navicat1中設定爲“使用mysql字符集”就相當於執行了一次“SET NAMES XXX”,這兒XXX似乎默認等於UTF-8,如果執行一次“show variables like 'character%';” 查詢得到圖navicat2。


    navicat1
     
    navicat2
     

    注意“character_set_database”值爲latin1,說明這是latin1的庫。因爲其保存的是GB內容所以查看是亂碼。於是如圖navicat3修改設置,於是中文顯示正常

    navicat3

    navicat5
     
     

 

 

三、項目中使用了PEAR::MDB2作爲數據庫連接方式,數據庫爲UTF-8編碼。測試中發現前臺頁面(UTF-8)中文有些是亂碼的,於是使用ems v4查看,同一個表中有不亂碼的有正常的。使用navicat查看,分別採用圖navicat1的設置以及圖navicat4的設置,亂碼情況正好相反——換個設置不亂碼的正常而正常會亂碼。

navicat1
 
navicat4

 

 

 

 

 

思考半天后來發現是MDB2連接時忘了使用“SET NAMES UTF8”命令查詢。因爲數據庫中存在原始數據所以和新插入數據的編碼實則是不一樣——前者是以UTF-8編碼保存在UTF-8的數據庫表中;後者是以被Latin1打包過的UTF-8數據保存在UTF-8數據庫中。在ems v4中無論客戶端是設定默認/utf8它都使用如下設定

SET character_set_client = utf8;
SET character_set_results = utf8;
SET character_set_connection = utf8;

 而navicat8中如果指定編碼格式則以上三項都是採用 Latin1編碼。。。其實就和ems v3採用的同樣策略。

 

知道了問題自然就知道了怎麼解決。不過看了PEAR手冊發現可以在定義dsn連接時就可以直接指定連接編碼方式:

$dsn = array(
  'phptype'=>DB_DATABASE, 
  'username'=>DB_USER, 
  'password'=>DB_PW,
  'hostspec'=>DB_HOST, 
  'database'=>DB_NAME, 
  'charset'=>'utf8');
 

有了最後那個值就相當於執行了一次 "SET NAMES UTF8" 命令。

 

總而言之:

  1. 儘量使用UTF-8作爲統一編碼,當前後臺數據庫之間數據流通時就無需特別轉碼也無需考慮亂碼,AJAX也無需考慮對中文打包轉碼問題。
  2. 後臺在初始化數據庫連接之後需要做一次查詢,內容爲“SET NAMES UTF8”,以保證mysql使用UTF-8作爲傳輸路徑三個部分的統一編碼而不是默認的Latin1。否則你建立UTF-8的數據庫但保存的內容卻是Latin1編碼格式(mysql自動在UTF-8外面包括了一層Latin1) 。

 

 

 

 

 

 

 

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