2009年2月17日 星期二

[php]UTF-8 轉 BIG5 的問題 + 解決方法

出處: http://www.ps3w.net/modules/psbb/?op=openthr&pos_id=1101

歡迎光臨 INABA 依&#33971;貝 有機保養莊園 <<-- 其中 BIG5 缺碼字 [蒳] 就是以 UnicodeHTML 編碼 -->> [&#33971;] 的方式 在網頁上顯示出正確的字形 -->> 蒳

&#33971; <<-- 其中 33971 <<-- 這個 10進位整數, 其實就是 由 U+84B3 <<-- 16進位數字 換算出來的數字 [wink] 。

以下是完整的自訂函式 utf8conv2charset() ...

<?php
function utf8conv2charset($utf8str, $charset='BIG5'){
mb_regex_encoding($charset); // 宣告 要進行 regex 的多位元編碼轉換格式 為 $charset
mb_substitute_character('long'); // 宣告 缺碼字改以U+16進位碼為標記取代
$utf8str = mb_convert_encoding($utf8str, $charset, 'UTF-8');
$utf8str = preg_replace('/U\+([0-9A-F]{4})/e', '"&#".intval("\\1",16).";"', $utf8str); // 將U+16進位碼標記轉換為UnicodeHTML碼
return $utf8str;
}
?>


這個函式 utf8conv2charset_c() 以及 前面那個直接用 mb_convert_encoding() 轉換的 utf8conv2charset() 不僅可用來 UTF-8 轉換 BIG5,UTF-8 轉成任何語系編碼都行,只要第二參數填上要轉換的目的編碼就可以了。

例如︰

UTF-8 轉換 GBK ...
utf8conv2charset('輸入UTF-8編碼文字資料', 'GBK'); 或 utf8conv2charset_c('輸入UTF-8編碼文字資料', 'GBK');

UTF-8 轉換 GB2312 ...
utf8conv2charset('輸入UTF-8編碼文字資料', 'GB2312'); 或 utf8conv2charset_c('輸入UTF-8編碼文字資料', 'GB2312');

UTF-8 轉換 日文碼 SHIFT-JIS
utf8conv2charset('輸入UTF-8編碼文字資料', 'SHIFT-JIS'); 或 utf8conv2charset_c('輸入UTF-8編碼文字資料', 'SHIFT-JIS');

... ... ...

只要目的編碼中有缺字就會改以 UnicodeHTML 碼 取代,可在任何編碼的網頁上顯示出正確的 Unicode 萬國碼字形。


請注意!

這個「解決方法二」 的函式 utf8conv2charset_c() 多個 c 意思是 一個 character (一次一個字) 他的轉碼方式主要是以 iconv() 一個個字組檢查是否有缺碼... 效率不如前面「解決方法一」的 utf8conv2charset()。

所以,
假如您的主機有支援 mb_convert_encoding() 就用 「解決方法一」的 utf8conv2charset(),沒支援時 不得已 才用 「解決方法二」的 utf8conv2charset_c()。

可以在者兩支函式再多加寫一小段程式碼自動判斷,
若有支援 就用 utf8conv2charset(); 無支援才用 utf8conv2charset_c();...

<?php
function utf8conv2charset_c($utf8str, $charset='BIG5'){
$stf8Charset = 'UTF-8'; $charset.= '//IGNORE';
$newCharsetStr = '';
$prefix = '&#';
$len = strlen($utf8str);
for($i=0;$i<$len;$i++){
$chrPos0 = substr($utf8str, $i, 1);
$chrASCII = ord($chrPos0);
if( $chrASCII < 0x80 ){ // 單一字碼 ASCII < 128 ; 二進制碼 1000 0000
$newCharsetStr.= $chrPos0; // 免轉碼 ...
}elseif( $chrASCII >= 0xc0 && $chrASCII < 0xe0 ){ // 雙字元碼 ASCII 192 to 223 ; 二進制碼 1100 0000 - 1101 1111
$chrBytes = 2; $addBytes = $chrBytes-1;
$newChrs = iconv($stf8Charset, $charset, substr($utf8str, $i, $chrBytes));
if(!$newChrs){ // 若 缺碼, 改轉換成 UnicodeHTML ...
$chrPos1 = substr($utf8str, $i+1, 1);
$newChrs = ( (ord($chrPos0)&0x1f) * 0x40 // [110xxxxx] 二進制碼遮罩去掉前3位元取後5位元, 再補後1字元碼6位元
+ (ord($chrPos1)&0x3f) // [10xxxxxx] 二進制碼遮罩去掉前2位元取後6位元
); // 以上移除UTF-8加長碼, 還原Unicode編碼, 再以 ASCII 加總 成為 10 進制整數
$newChrs = $prefix.$newChrs.';'; // 加 前置字元 &# 及後置分號 ; 成為 UnicodeHTML 碼
}
$newCharsetStr.= $newChrs;
$i+= $addBytes;
}elseif( $chrASCII >= 0xe0 && $chrASCII < 0xf0 ){ // 三字元碼 ASCII 224 to 239 ; 二進制碼 1110 0000 - 1110 1111
$chrBytes = 3; $addBytes = $chrBytes-1;
$newChrs = iconv($stf8Charset, $charset, substr($utf8str, $i, $chrBytes));
if(!$newChrs){
$chrPos1 = substr($utf8str, $i+1, 1);
$chrPos2 = substr($utf8str, $i+2, 1);
$newChrs = ( (ord($chrPos0)&0x0f) * 0x1000 // [1110xxxx] 二進制碼遮罩去掉前4位元取後4位元, 再補後2字元碼12位元
+ (ord($chrPos1)&0x3f) * 0x40 // [10xxxxxx] 二進制碼遮罩去掉前2位元取後6位元, 再補後1字元碼 6位元
+ (ord($chrPos2)&0x3f)
);
$newChrs = $prefix.$newChrs.';';
}
$newCharsetStr.= $newChrs;
$i+= $addBytes;
}elseif( $chrASCII >= 0xf0 && $chrASCII < 0xf8 ) { // 四字元碼 ASCII 240 to 247 ; 二進制碼 1111 0000 - 1111 0111
$chrBytes = 4; $addBytes = $chrBytes-1;
$newChrs = iconv($stf8Charset, $charset, substr($utf8str, $i, $chrBytes));
if(!$newChrs){
$chrPos1 = substr($utf8str, $i+1, 1);
$chrPos2 = substr($utf8str, $i+2, 1);
$chrPos3 = substr($utf8str, $i+3, 1);
$newChrs = ( (ord($chrPos0)&0x07) * 0x40000 // [11110xxx] 二進制碼遮罩去掉前5位元取後3位元, 再補後3字元碼18位元
+ (ord($chrPos1)&0x3f) * 0x1000 // [10xxxxxx] 二進制碼遮罩去掉前2位元取後6位元, 再補後2字元碼12位元
+ (ord($chrPos2)&0x3f) * 0x40
+ (ord($chrPos3)&0x3f)
);
$newChrs = $prefix.$newChrs.';';
}
$newCharsetStr.= $newChrs;
$i+= $addBytes;
}elseif($chrASCII>=0xf8 && $chrASCII<0xfb){ // 五字元碼 ASCII 248 to 251 ; 二進制碼 1111 1000 - 1111 1011
$chrBytes = 5; $addBytes = $chrBytes-1;
$newChrs = iconv($stf8Charset, $charset, substr($utf8str, $i, $chrBytes));
if(!$newChrs){
$chrPos1 = substr($utf8str, $i+1, 1);
$chrPos2 = substr($utf8str, $i+2, 1);
$chrPos3 = substr($utf8str, $i+3, 1);
$chrPos4 = substr($utf8str, $i+4, 1);
$newChrs = ( (ord($chrPos0)&0x03) * 0x100000 // [111110xx] 二進制碼遮罩去掉前6位元取後2位元, 再補後4字元碼24位元
+ (ord($chrPos1)&0x3f) * 0x40000 // [10xxxxxx] 二進制碼遮罩去掉前2位元取後6位元, 再補後3字元碼18位元
+ (ord($chrPos2)&0x3f) * 0x1000
+ (ord($chrPos3)&0x3f) * 0x40
+ (ord($chrPos4)&0x3f)
);
$newChrs = $prefix.$newChrs.';';
}
$newCharsetStr.= $newChrs;
$i+= $addBytes;
}elseif($chrASCII>=0xfb && $chrASCII<0xfd){ // 六字元碼 ASCII 252 to 253 ; 二進制碼 1111 1100 - 1111 1101
$chrBytes = 6; $addBytes = $chrBytes-1;
$newChrs = iconv($stf8Charset, $charset, substr($utf8str, $i, $chrBytes));
if(!$newChrs){
$chrPos1 = substr($utf8str, $i+1, 1);
$chrPos2 = substr($utf8str, $i+2, 1);
$chrPos3 = substr($utf8str, $i+3, 1);
$chrPos4 = substr($utf8str, $i+4, 1);
$chrPos5 = substr($utf8str, $i+5, 1);
$newChrs = ( (ord($chrPos0)&0x01) * 0xA00000 // [1111110x] 二進制碼遮罩去掉前7位元取後1位元, 再補後5字元碼30位元
+ (ord($chrPos1)&0x3f) * 0x100000 // [10xxxxxx] 二進制碼遮罩去掉前2位元取後6位元, 再補後4字元碼24位元
+ (ord($chrPos2)&0x3f) * 0x40000
+ (ord($chrPos3)&0x3f) * 0x1000
+ (ord($chrPos4)&0x3f) * 0x40
+ (ord($chrPos5)&0x3f)
);
$newChrs = $prefix.$newChrs.';';
}
$newCharsetStr.= $newChrs;
$i+= $addBytes;
}else{ // 通常, 若輸入的是標準 UTF-8 編碼文字, 就不應該會有這個錯誤狀況發生; 這部份是預防輸入的UTF-8編碼文字資料內容本身的錯誤
$newCharsetStr.= '{[U+????]}';
}
}
return $newCharsetStr;
}

?>

【下列文章您可能也有興趣】

沒有留言: