與「CJK字符」和「UTF-8編碼」有關的一些文字

現在是2009年5月4日淩晨。時隔半年,終于着手編輯準備完成這些文字了。原來的標題是下面這張圖片。

當時是這樣寫的:

我暫時還想不出什麽合適的文字標題來,剛好又發現也可以使用圖片來作標題,見此,就隨便偷下懶兒了。

這圖片上的文字,其實是再前一篇雜碎兒中的内容(圖片加了顔色),見此,只不過在那裏很多人見到的或者要麽是類似Safari或者要麽是類似??或者要麽是類似Opera等等。我現在要整理出來的文字就是和這有些關係;胡亂研究這個著實用掉了多半個周末的時間,也就是一天多。

以前在《康熙字典》見到一個字,𠀤(圖片:Bing),在GB2312字符集和GBK字符集中沒有找到,自己就以爲在電腦上是打不出這個字來的,很顯然是我一廂情願的錯誤。後來在漢典無意中見到了這個字,很興奮,當時確實應該是的。然後查了些資料,就引出下面這些東西來。

當時可能是嫌麻煩,後來又太懶散,就一直拖到現在,結果很多計劃寫的内容都忘記了。還好,當時留了個提綱下來;不過,現在並不打算完全依照提綱的思路來寫。

未完成,暫列提綱:
0.系統環境。
1.相關漢字編碼資料。
2.無法Post。Mysql問題。兩種方式行不通。問題原因在編碼。
3.Unicode編碼。十進制與十六進制。0x20000=131072。
4.不同瀏覽器的不同解釋。

直接切入要點。

1.對發現的問題的一個不甚準確的描述就是,「CJK拓展B區的字符,無法在charset=UTF-8的頁面Post進數據庫」。擧個例子,比如我現在用的這個WordPress程序,後臺設置「Encoding for pages and feeds」為UTF-8時,是不能直接輸入像「𠀤」這樣的CJK-B字符的,需要先轉換成「𠀤」的形式;如果設置成GB2312、Big5等類型編碼,則可直接輸入並Post進數據庫。這些,也在不同編碼的PHPWind論壇程序上得到了證實。

2.查閲了一些資料,基於現有代碼,改寫出一個「在綫計算字符UTF-8編碼」的PHP程序,支持CJK-A和CJK-B。見『在綫計算字符UTF-8編碼的測試程序』。核心代碼如下。

Download: UTF-8.php
  1. <div style="width: 360px;">
  2. <form method="post" action="#&action=test" name="test" id="test">
  3. <textarea name="content" id="content" cols="48" rows="8" value="">請輸入任意字符進行測試。</textarea>
  4. <input type="submit" value="Begin Test" />
  5. </form>
  6. </div>
  7.  
  8. <?php
  9. function str_split_utf8($str) {
  10.     $split = 1;
  11.     $array = array();
  12.     for ($i = 0; $i < strlen($str);) {
  13.         $value = ord($str[$i]);
  14.         if ($value > 127) {
  15.             if ($value >= 192 && $value <= 223)
  16.                 $split = 2;
  17.             elseif ($value >= 224 && $value <= 239)
  18.                 $split = 3;
  19.             elseif ($value >= 240 && $value <= 247)
  20.                 $split = 4;
  21.         } else {
  22.             $split = 1;
  23.         }
  24.         $key = NULL;
  25.         for ($j = 0; $j < $split; $j++, $i++) {
  26.             $key .= $str[$i];
  27.         }
  28.         array_push($array, $key);
  29.     }
  30.     return $array;
  31. }
  32.  
  33. function getUnicodeFromOneUTF8($word) {  
  34.   $arr = str_split($word);
  35.   $bin_str = '';  
  36.   foreach ($arr as $value)  
  37.       $bin_str .= decbin(ord($value));
  38.     if (strlen($bin_str) <= 8) {  
  39.           return "字符:&#".bindec($bin_str)."; 1字節 十進制:".bindec($bin_str)." 十六進制:".dechex(bindec($bin_str))."&#13;";
  40.     } elseif (strlen($bin_str)==16) {
  41.             $bin_str = preg_replace('/^.{3}(.{5}).{2}(.{6})$/','$1$2', $bin_str)
  42.           return "字符:&#".bindec($bin_str)."; 2字節 十進制:".bindec($bin_str)." 十六進制:".dechex(bindec($bin_str))."&#13;";
  43.     } elseif (strlen($bin_str)==24) {
  44.             $bin_str = preg_replace('/^.{4}(.{4}).{2}(.{6}).{2}(.{6})$/','$1$2$3', $bin_str)
  45.           return "字符:&#".bindec($bin_str)."; 3字節 十進制:".bindec($bin_str)." 十六進制:".dechex(bindec($bin_str))."&#13;";
  46.     } elseif (strlen($bin_str) == 32) {  
  47.           $bin_str = preg_replace('/^.{5}(.{3}).{2}(.{6}).{2}(.{6}).{2}(.{6})$/','$1$2$3$4', $bin_str);  
  48.           return "字符:&#".bindec($bin_str)."; 4字節 十進制:".bindec($bin_str)." 十六進制:".dechex(bindec($bin_str))."&#13;";
  49.     }   
  50. }
  51.  
  52. $action = $_REQUEST["action"];
  53. switch ($action) {    
  54. case "test" :
  55.     $string = $_POST["content"];
  56.     if (!$string) {
  57.          echo "<pre>請輸入字符</pre>";
  58.       exit;   
  59.     }
  60.     $array_temp = str_split_utf8($string);
  61.     echo "<pre>";
  62.         foreach ($array_temp as $word_temp) {
  63.             echo getUnicodeFromOneUTF8($word_temp);
  64.         }
  65.     echo "</pre>";
  66.     break;
  67. default :
  68.     break;
  69. } 
  70. ?>

3.早期版本的Google Chrome瀏覽器,在處理CJK-B拓展區字符時存在Bug;不過現在版本(1.0.154.59)似乎已經修正了。我將當時發現的問題簡單描述如下(U+20000始);因當前系統中Google Chrome瀏覽器已更新至高版本,故以下描述僅凴印象,未予驗證。

字符編碼(A)               Chrome顯示字符編碼(B)
0x20000                      0x20000
0x20001                      0x20000
0x20002                      0x20001
0x20003                      0x20001
0x20004                      0x20002
0x20005                      0x20002
0x20006                      0x20003
0x20007                      0x20003
0x20008                      0x20004
0x20009                      0x20004
0x20010                      0x20005
0x20011                      0x20005
0x20012                      0x20006
0x20013                      0x20006
0x20014                      0x20007
0x20015                      0x20008
…………                         …………

簡單來看,大致是B=|(A+20000)/2|的關係。

就這樣了。

Leave a Reply