close

數位工作這個欄目是我新開的, 主要是把我工作上一些問題的解決過程寫下來. 目的當然是適合自己作參考, 當然, 也是怕朋友同事來問, 還要再講一遍. 不過必須強調的是, 這裡寫的不是什麼秘技, 也不一定是新技術, 純粹是經驗談, 所以如果是老掉牙的問題請不要笑我, 基本上, 我只是圖個方便而已.


很多人或公司, 包括可敬的Google, 所使用的資料庫平台都是MySQL, 當然我也是: 免費又好裝嘛 . 而且它原先並非設計給mission critical的系統用, 所以很簡單, 不需要什麼高深的概念. 不過, 到MySQL 4.1開始, 這個簡單性被打破了. 也許是為了與M$SQL競爭, 或是為了技術上再進一步, 它也開始有了transection的概念(簡單來說, 就是資料改過必有痕跡, 也可以回復到改前的狀態), 以及有charset/collation的機構, 在伺服器上就可以處理不同字元集的資料比對動作.
以上, 都是好事. 但遇到麻煩的中日韓雙位元字集, 使用經驗就必須修正囉.


這裡我所謂的直覺式, 是指在不需要動到MySQL的安裝設定, 或改掉之前的使用經驗, 所找出來最佳的解決方式. 一言以蔽之, 就是懶而已. 所以太複雜的設定不在這裡敘述. 在行文開始前, 假設MySQL 4.1(或以上)是以預設的字元集與比對方式安裝, 包括資料庫, 資料表, 欄位的宣告等等, 都不去設定特有的字元集. 所以, 底下的方式適用於字元集為utf-8, 比對方式是utf8_unicode_ci(大小寫不分).


一般在使用MySQL時, 會用到中文的地方, 大概就是select/insert/update等, 而我常用的資料庫存取方式, 大概就mysql命令行, phpMyAdmin與PHP的mysql介面. 其他像jsp, 或php的mysqli, 就再討論了. 此外, 以下講的中文是以BIG5編碼的繁體中文為主, 其他如簡體中文或甚至日韓文, 應該依樣畫葫蘆即可.


MySQL命令列


一般如果有用中文進UNIX CONSOLE去執行mysql命令列的時機, 大概就是用Server本身的terminal, 或其他機器的remote console來連. 過去要維護一些中文資料, 這些terminal/console至少都會支援BIG5的中文輸入與瀏覽. 由於MySQL 4.1以上預設是以utf-8作為內部儲存/比對方式, 所以如果像以前直接塞資料的話, 那就會變亂碼. 解決方式是, 在一進命令列時輸入:


SET NAMES big5;


之後, 就可以用BIG5編碼的中文塞資料, select也看得到正常的字元(註1)


phpMyAdmin


我想不少人跟我一樣, 會裝個phpMyAdmin來維護資料庫, 全部用網頁介面呈現, 使用超方便. 不過它的預設安裝方式, 網頁部分會跟著瀏覽器的預設字元集(在台灣大都是BIG5), 後端連資料庫也是用資料庫的預設字元集(在本文例中是utf-8). 到此大家就知道, 如果不改, 中文資料如果塞進去或動到, 就會哭到沒目屎 .
解決方式是, 除了裝最新版的phpMyAdmin之外, 在config.inc.php裡加上底下數行:


$cfg['AllowAnywhereRecoding'] = true;
$cfg['DefaultCharset'] = 'utf-8';
$cfg['RecodingEngine'] = 'iconv';
$cfg['IconvExtraParams'] = '//TRANSLIT';


意思是, 進資料庫的query都會轉成utf-8, 出資料庫則會由utf-8轉回目前網頁的預設編碼. 如此一來就沒問題了.


PHP


之前為了解決中文字與MySQL控制碼互衝的問題, 已經有人建議用utf-8來作網頁的預設編碼. 如此一來就不會有資料庫中文變亂碼, 或弄不好多了一堆反斜線'\'的問題 . 如果MySQL換成4.1版以上, 用這個方法就沒什麼問題(註2). 不過如果本來就有一堆用BIG5中文編的網頁, 或是為了網頁製作方便, 不想打完又得轉碼才能把網頁貼上去的話, 那就小麻煩了.
如果要沿用BIG5當成網頁編碼方式的話, 有兩種方法可以與新的MySQL溝通:
1) 中文字在塞入資料庫前, 先用iconv轉成utf-8; 抓取中文資料後在呈現之前, 也要記得用iconv轉回來;
2) 在mysql_connect成功之後, 先作下面的query:


mysql_query( "SET NAMES big5" );


跟使用命令列一樣, 就把輸入輸出環境設為big5, 在query時就會自動轉換了(註3).


以上的方式都已經測試無誤, 請安心服用... 不過還是要看一下文末的附註


[註1]上面只提到塞/取中文資料的方式, 但如果用mysql本身的字串相關函數, 比如取得字串長度, 則必須注意. 比如在資料表x裡y欄位有一筆資料"AH-285之戀", 要算它的長度, 在之前是用以下的query:


select length(y) from x;


不過答案卻變成12---因為在utf-8裡, 一個中文字用掉3個byte. 所以在使用字串函數之前, 必須先轉碼再用:


select length(convert(y using big5)) from x;


答案就會是10. 其他函數則照這個樣子試試看.


[註2]在某些瀏覽器, 如IE, 有時會看不懂網頁meta標籤所設定的字元集, 而會用瀏覽器的預設字元集, 這時有可能你把網頁轉編碼成utf-8後, 輸入的字串傳到form所指定的網頁時會變成瀏覽器的預設字元集, 比如BIG5, 那樣有改等於沒改. 比較安全的方式是, 不管哪個網頁, 編碼除了用meta來設定之外, 最好再送出content-type的表頭. 比如在php裡, 網頁一開頭便下:


header( "Content-type: text/html; charset=utf-8");


這樣可以保證瀏覽器會轉成正確的編碼.


[註3]其實在PHP裡, php.ini預設是會在form資料上傳時, 雞婆地加上反斜線. 但這只是方便在早期的MySQL(用lantin1編碼), 以及上傳資料到塞資料庫中間不需要作任何處理時用. 這樣寫程式時就在要不要加反斜線傷腦筋. 在PHP官網裡剛好有人放上一個解決SQL-injection hack的方式, 剛好可以改來作這樣的處理(SQL injection hack是指有壞人在輸入文字時故意輸入SQL部分指令, 讓程式誤傳query而取得一些機密資料或損害資料庫的動作), 我把它稍稍修改一下, 寫成一個function來用:


function fnQuote_smart( $value ) {
  if (get_magic_quotes_gpc()) {
    $value = stripslashes( $value );
    $value = "'" . mysql_real_escape_string( $value ) . "'";
  }
  return $value;
}


query的sql command需要稍稍修正為:


$sql = sprintf( "insert y values ( %s )", fnQuote_smart( $text ) );


要注意, 那個欄位確定為char/varchar屬性, 而且在值的前後不要加單引號('), 因為單引號已在fnQuote_smart加上了. 這個function必須要在連上資料庫後才能用, 而且也許還有BUG, 大家要試過. 至於為什麼要用mysql_real_escape_string而不用addslashes, 這是因為在各種不同的字元集裡, 不見得每個有反斜線的字元都需要再加一個以免被吃掉, 所以用mysql_real_escape_string, 讓它去偵測mysql server的設定, 看是什麼字元需要加上什麼樣的逸出字元, 才能保證萬無一失.

arrow
arrow
    全站熱搜

    bhchou0102 發表在 痞客邦 留言(0) 人氣()