BIG5字數不足是正體中文環境存在已久的問題,在Unicode尚未普及前,出現過許多各顯神通的解決方案,自行造字、廠商推出的擴充字集(例如: Microsoft CP950、Big5_eten)、BIG5+、BIG5E、CNS11643。(註: 想進一步了解的話有兩篇不錯的文章12)

近年來Unicode已漸漸成為各家系統及平台都支援的主流標準,雖然無法滿足全部的中文字(例如: 姓名裡的自創字以及古文史籍中的罕見字),Unicode幾乎是全球解決跨國多語系的大一統終極方案,也獲得大部分開發人員的認同。

很不幸地,許多歷史悠久規模龐大牽扯複雜盤根錯結的系統,要更動文字編碼形同動搖國本,提案更換Unicode的人勢必要抱著捨身取義雖千萬人吾往矣的決心不可,在願意去掛鈴鐺的勇敢老鼠出現之前,我們還是要學著與這些舊時代的中文編碼解決方案共處。

在證券業界,主要是由台灣集保公司統一制定罕用字集作為資料交換的標準。我們今天的練習題便是如何用ADO.NET顯示ORACLE資料庫VARCHAR裡的集保罕用字。

進行以下測試時,請先至集保結算所網站下載安裝罕用字集: (【其他下載資料/華康中文罕用字集】)

裝好罕用字集後,我們可以用以下的手法建一個TDCCCHAR資料表 ,並填入"犇"這個字進VARCHAR2欄位。(注意: 要輸入罕用字集版本而非Unicode裡的,如果你不知怎麼輸入,可以用Encoding.GetEncoding("big5").GetString(new byte[] { 0x9f, 0x6e })產生再剪貼)

好了,來寫一段ADO.NET程式把犇牽出來吧!

<%@ Page Language="C#" %>
<%@ Import Namespace = "System.Data.OracleClient" %>
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        using (OracleConnection cn = new OracleConnection(
            ConfigurationManager.ConnectionStrings["myOracle"].ConnectionString))
        {
            string sql = @"SELECT VC FROM TDCCCHAR";
            OracleCommand cmd = new OracleCommand(sql, cn);
            cn.Open();
            OracleDataReader dr = cmd.ExecuteReader();
            while (dr.Read()) 
            {
                string s = dr["VC"].ToString();
                Response.Write("<li>byte[]: " +
                    BitConverter.ToString(
                        Encoding.GetEncoding("big5").GetBytes(s)
                    ));
                Response.Write("<li>string:" + s);
            }
            cn.Close();
            Response.End();
        }
    }
</script>

事與願違,我們得到的結果會是: byte[]: 3F string:�

依我的推敲,集保罕字因為在Unicode中沒有適當的對應,而.NET的核心早已是Unicode,這些由ORACLE讀取的結果一律要強轉成Unicode,罕用字便會被置換成3F(?),在DataReader取出前就打上馬賽克(而且還不是薄碼哩),之後在.NET裡就算使出渾身解數也不可能還原。

試了許久,一直找不到相關設定可以讓System.Data.OracleClient或ODP.NET保留VARCHAR欄位的原始編碼,也無法改用byte[]/Stream方式讀取字串原始內容,看來此路不通。(知道有巧門的朋友請出個聲吧!)

山不轉路轉,我倒是找到一個解決方法,可以閃過罕用字被置換成問號的問題。先利用ORACLE的RAWTOHEX函數將字串轉成16進位碼,再從.NET端解碼。程式碼修改如下:

            string sql = @"SELECT RAWTOHEX(VC) AS HEX FROM TDCCCHAR";
            OracleCommand cmd = new OracleCommand(sql, cn);
            cn.Open();
            OracleDataReader dr = cmd.ExecuteReader();
            while (dr.Read()) 
            {
                string hex = dr["HEX"].ToString();
                byte[] buff = new byte[hex.Length / 2];
                for (int i = 0; i < hex.Length / 2; i++)
                {
                    buff[i] = byte.Parse(hex.Substring(i * 2, 2), 
                        System.Globalization.NumberStyles.AllowHexSpecifier);
                }
                string s = Encoding.GetEncoding("big5").GetString(buff);                
                Response.Write("<li>byte[]: " +
                    BitConverter.ToString(
                        Encoding.GetEncoding("big5").GetBytes(s)
                    ));
                Response.Write("<li>string:" + s);
            }

很神奇地,在已安裝罕用字集的IE上,就可順利看到byte[]: 9F-6E string: 犇;但在一般的Windows上,犇卻還是出不來。(因為Unicode裡並未對應Big5 %9f%6e的綠故)

真正的解決之道,應該還是得將罕用字集的罕字一一對應成Unicode裡有的文字,讓所有的文字回歸統一的標準。這個好問題,留待下回分解。


Comments

# by bean

謝謝大大的文章. 最近小弟也是在煩這個問題, 在winform的程式就是沒有辦法顯示一些big-5碼罕字....我猜是因為.net原生是unicode, 看完大大這篇文章就更加確定我的懷疑了..囧

Post a comment