如果引用或轉貼,麻煩註明出處與本網誌連結,否則視為侵權。

2019年4月12日

[中文編碼問題] 繁簡體中文字都可以輸入, 儲存SQL Server DB(2019以前的版本)並顯示在PHP網頁上 (尚無正確的方法)

作者: Fred F.M. Wang (FW知識瑣記) 日期: 20190412

花了一天的時間研究UTF-8網頁繁簡體並存的問題,怎樣讓繁簡體都可以輸入,儲存入資料庫並正確地顯示在網頁上。

有些人覺得很簡單阿,只要將網頁上輸入的UTF-8繁或簡體中文字串,直接儲存進以UTF-8編碼的資料庫資料欄位就可以了,反之,從資料庫讀取也很直接,沒問題的。

問題出在資料庫,我在mysql資料庫,用上面方式存取,繁體簡體字都沒問題。 但是使用MS SQL Server就是不行。

如果要直接儲存UTF-8不要轉碼, 在SQL Server(2019以前的版本)中辦得到嗎? 答案是否定的。

找到一篇微軟官網的文章  "說明SQL Server 中儲存 UTF-8 資料", 提到"某些應用程式 (特別是 Web 應用程式) 必須處理以 UTF-8 編碼方法所編碼的 Unicode 資料。SQL Server 7.0 和 SQL Server 2000 使用不同的 Unicode 編碼方式 (UCS-2),無法將 UTF-8 識別為有效的字元資料。" , "Microsoft Windows NT、SQL Server、Java、COM 和 SQL Server ODBC 驅動程式以及 OLEDB 提供者在內部都以 UCS-2 代表 Unicode 資料。"

SQL Server原來是不支援UTF-8的,直到SQL Server 2019才支援 (微軟終於覺醒了?!)

那麼網頁(UTF-8)儲存入SQL Server DB改用 mb_convert_encoding($data, "UCS-2","UTF-8"); 轉成UCS-2編碼儲存到DB中,從SQL Server DB讀出顯示到網頁(UTF-8)時, 用 mb_convert_encoding($data,"UTF-8", "UCS-2"); 轉成UTF-8顯示。
這樣是否就可行了呢?  結果還是失敗, 仍然無法正確轉換所有的繁簡中文。





感謝internet上許多網友無私分享許多的文章,讓自己找到不少線索,不過可能因為環境不同,需要花一些時間嘗試,找到合適的方法來解決這個問題。

我的系統環境

資料庫 : MS SQL Server 2008
資料庫編碼(SQL Server定序) : Chinese_Taiwan_Stroke_BIN

Web System : PHP + Codeigniter
Web System編碼 : UTF-8


方法 :
1 將會輸入中文字的欄位編碼由Chinese_Taiwan_Stroke_CI_AS改為Chinese_PRC_CI_AS (當然, 欄位型態必須是nchar, nvarchar, ntext這種帶n開頭的型態)

2  儲存時判斷轉入的字串是不是簡體字,如果是就用mb_convert_encoding($inputstr,"cp936", "UTF-8") 轉為cp936碼, 否則視為繁體字 用mb_convert_encoding($inputstr,"BIG5", "UTF-8") 轉為BIG5碼 再儲存到資料庫中
*** 簡體字判斷方法 : 參考 "PHP 判斷 UTF-8 字串是 簡體 或 繁體中文"

3 由資料庫讀出時判斷是不是簡體字,如果是就用mb_convert_encoding($data, "UTF-8", "CP936")轉為UTF-8顯示,否則視為繁體字 用mb_convert_encoding($data, "UTF-8", "BIG5")轉為UTF-8顯示
    判斷方法 :  用mb_detect_encoding
function encode($str) {
 $codes = array("ASCII","GB2312","BIG5","UTF-8");
 $encode = mb_detect_encoding($str,$codes); 
 return $encode;
}
 
結果 :
1 繁簡體中文都可以儲存到DB中
2 "測試" 未判斷成繁體字, 轉成簡體字"代刚", "知識"也沒有正確判斷是BIG5, 變成亂碼

問題關鍵是如何判斷從資料庫讀出的文字是繁體字或簡體字? 用mb_detect_encoding顯然無法正確判斷

20190415
用不同編碼的字串長度來實驗, 如下 :

 發現從判斷從資料庫讀出的字串,如果是繁體中文字串, mb_strlen($data, "UTF-8")與mb_strlen($data, "CP950")的結果不同, 而簡體中文字串則相同,因此, 可已根據這種規則判斷是簡體中文或繁體中文。

我找到方法了, 修改上面第三點,判斷字串長度

$len1 = mb_strlen($str, "UTF-8");
$len2 = mb_strlen($str, "CP950");
if($len1 === $len2) { // 簡體字  
 $code = "CP936";
} else {   // 繁體字
 $code = "BIG5";
} 

如上面方式, 兩種長度相等表示是簡體字, 不相等表示是繁體字
判斷出來後再 將資料轉成UTF-8 ==> mb_convert_encoding($data, "UTF-8", $code);
這樣就正確了 (限目前我測試的內容)



以上為目前測試的結果, 但是不表示所有的中文字都能100%的辨別, 筆者發現更長的字串,上面判斷法則就會失敗, 還需要更多的測試與修正, 另外, 繁簡夾雜的字串也會有問題

筆者最後決定資料庫改在mySQL,就不會有此傷腦筋的問題了。使用SQL Server 2008也可以升級為SQL Server 2019就沒這個問題了。-20190426 Fred


相關文章 :
1. [繁簡中文並存問題]PHP Web應用系統繁簡中文並存問題研究
2. [繁簡中文並存問題]網頁應用上傳檔案檔名變成亂碼的解決方法

沒有留言:

張貼留言

歡迎提供意見, 謝謝 (註 : 留言經過版主審核通過才會發布)