2009年11月19日 星期四

在PowerBuilder下轉換簡繁體中文字

聲明:這是在Windows XP SP3繁體中文版環境底下的方式,不一定適用於英文版外掛繁體中文套件及Windows XP簡體版。

這是PB10的版本,如果你是PB10以下的版本,請注意external function宣告的方式不需要使用";ansi"。

首先宣告 instant 變數 :

// 區域語系
Constant    long   LCMAP_TRADITIONAL_CHINESE=67108864
Constant    long   LCMAP_SIMPLIFIED_CHINESE=33554432
// 編碼方式
constant Uint CP_ANSI          = 0         // default to ANSI code page
constant Uint CP_OEM           = 1         // default to OEM code page
constant Uint CP_MAC           = 2         // MAC code page
constant Uint CP_TANSI         = 3         // THREAD ANSI code page 
constant Uint CP_SYMB          = 42        // SYMBOL
constant Uint CP_WIN874        = 874       // "windows-874" "泰文 (Windows)"
constant Uint CP_2022_JP_SJ    = 932       // "iso-2022-jp" "日文 (Shift-JIS)"
constant Uint CP_GB2312        = 936       // 簡體中文   (GB2312)
constant Uint CP_KR            = 949       // "euc-kr" "韓文"
constant Uint CP_BIG5          = 950       // 950 為 "big5" "繁體中文 (Big5)"
constant Uint CP_UNICODE_BE    = 1201      // "unicodeFFFE" "Unicode (Big-Endian)"
constant Uint CP_WIN1258       = 1258      // "windows-1258" "越南文 (Windows)"
constant Uint CP_USERD         = 50000     // "x-user-defined" "使用者定義"
constant Uint CP_AUTO          = 50001     // "_autodetect_all" "自動選取"
constant Uint CP_2022_JP_J     = 50220     // "iso-2022-jp" "日文 (JIS)"
constant Uint CP_2022_KR       = 50225     //  "iso-2022-kr" "韓文 (ISO)" 
constant Uint CP_JP_AUTO       = 50932     // "_autodetect" "日文 (自動選取)" 
constant Uint CP_KR_AUTO       = 50949     // "_autodetect_kr" "韓文 (自動選取)"
constant Uint CP_JP_EUC        = 51932     // "euc-jp" "日文 (EUC)" 
constant Uint CP_KR_EUC        = 51949     // "euc-kr" "韓文 (EUC)" 
constant Uint CP_GB2312_HZ     = 52936     // "hz-gb-2312" "簡體中文 (HZ)"  
constant Uint CP_UTF7          = 65000     // "utf-7" "Unicode (UTF-7)"
constant Uint CP_UTF8          = 65001     // Unicode   (UTF-8)

然後是 external Function :


// 此處API是以符合PB10,及使用Blob傳遞資料方式改寫,略與MSDN上的有點出入
function  ulong  MultiByteToWideChar(UINT  CodePage,  ulong  dwFlags , ref blob lpMultiByteStr , int  cbMultiByte, ref blob lpWideCharStr , ulong cchWideChar)   library  "kernel32.dll" alias for "MultiByteToWideChar"
function  ulong  WideCharToMultiByte(UINT  CodePage,  ulong  dwFlags,  ref blob  lpWideCharStr, ulong  cchWideChar,  ref   blob   lpMultiByteStr, ulong   cbMultiByte, ref   string   lpUsedDefaultChar , ref   boolean lpUsedDefaultChar) LIBRARY "kernel32.dll" alias for"WideCharToMultiByte;Ansi"
function ulong LCMapString(ulong Locale , ulong dwMapFlags , ref blob lpSrcStr , ulong cchSrc , ref blob lpDestStr , ulong cchDest) LIBRARY "kernel32.dll" ALIAS FOR "LCMapStringW"

如果你要將簡體轉換為繁體:
定義一個Function : of_BIG52GBK(blob ablb_src) returns blob

uLong        lul_nlen , lul_swp
Blob        lblb_wzs , lblb_map , lblb_dst
String        ls_default    = "?" , ls_null
Boolean    lb_udc    = False

setnull(ls_null)

if isnull(ablb_src) then Return blob(" ")

// 取得blob(ansi) 轉成 unicode 所需要的長度
lul_nlen    = Len(ablb_src)

SetNull(lblb_wzs)
lul_swp    = MultiByteToWideChar(CP_GB2312 , 0 , ablb_src , -1 , lblb_wzs , 0)
lblb_wzs    = blob(space(lul_swp))
//轉換Gb碼到Unicode碼,使用了API函數MultiByteToWideChar
MultiByteToWideChar(CP_GB2312 , 0 , ablb_src , -1 , lblb_wzs , lul_nlen)

lul_nlen    = Len(lblb_wzs)
lblb_map    = blob(space(lul_nlen) , EncodingANSI!)
//轉換Gb碼簡體到Gb碼繁體,使用API函數LCMapString,台灣碼表1028,大陸碼表2052
LCMapString(1028 , LCMAP_TRADITIONAL_CHINESE , lblb_wzs , -1 , lblb_map , lul_nlen)

SetNull(lblb_dst)
lul_swp    = WideCharToMultiByte(CP_BIG5 , 0 , lblb_map , -1 , lblb_dst , 0 , ls_null , lb_udc)
lblb_dst =  blob(space(lul_swp)   , EncodingANSI!)
//轉換Unicode碼到Big5碼,使用API函數WideCharToMultiByte
WideCharToMultiByte(CP_BIG5 , 0 , lblb_map , -1 , lblb_dst , lul_swp , ls_default ,  lb_udc)

Return lblb_dst


如果你要將繁體轉換為簡體:
定義一個Function : of_GBK2BIG5(blob ablb_src) returns blob

uLong        lul_nlen
Blob        lblb_wzs , lblb_map , lblb_dst
String        ls_default    = "?"
Boolean    lb_udc    = false

if isnull(ablb_src) then Return blob(" ")

// 取得blob(ansi) 轉成 unicode 所需要的長度
lul_nlen    = Len(ablb_src)

lblb_wzs    = blob(space(lul_nlen))
//轉換BIG5碼到Unicode碼,使用了API函數MultiByteToWideChar
MultiByteToWideChar(CP_BIG5 , 0 , ablb_src , -1 , lblb_wzs , lul_nlen)

lblb_map    = blob(space(lul_nlen))
//轉換Gb碼簡體到Gb碼繁體,使用API函數LCMapString,台灣碼表1028,大陸碼表2052
LCMapString(1028 , LCMAP_SIMPLIFIED_CHINESE , lblb_wzs , -1 , lblb_map , lul_nlen)

lblb_dst =  blob(space(lul_nlen)   , EncodingANSI!)
//轉換Unicode碼到BGK碼,使用API函數WideCharToMultiByte
WideCharToMultiByte(CP_GB2312 , 0 , lblb_map , -1 , lblb_dst , lul_nlen , ls_default ,  lb_udc)
      
Return lblb_dst


說明:
  在 Function 中全程都以Blob處理,原因在於PowerBuilder 的 String 變數本身如果你將取得的資料放入 String 變數後,它會嘗試去進行文字編碼,而這個編碼會因應OS本身設定的區域進行字碼對照轉換。在PB10以下的版本,假如從檔案讀取文件內容,則要以StreamMode來讀取成blob,如果以LineMode讀取為String,它會以 ANSI 原則去對照 繁體中文字碼表,因此如果找不到相關字碼則會將該字碼呈現 ? 號(0x3F),因此將失去原本取得的簡體字來源正確性。但是如果以PB10(含)以後的版本以LineMode或是TextMode讀取 String,則它會試圖轉換為 unicode ,但由於文件檔案內容本身是 ANSI 編碼,則會先轉為 UNICODE 對應碼,再試圖對照繁體中文字碼表,則結果可想而知,整個 String 內容將完全走樣。
所以處理其他編碼字串時,首先從來源(不管是Internet或是File)最好讀取時皆以 Blob 變數全成處理,直到編碼轉換完成為止。如此可以正確保留來源資料的正確性。

簡單示範一下從網路取得資料後的轉換,對象是大陸網站的HTML,通常的編碼都是GB2312:

inet  linet_1
n_internetresult lir_data //這是繼承 PB 的 internetresult 物件
blob lblb_htm
String ls_url , ls_htm

linet_1 = Create inet
lir_date = Create n_internetresult

ls_url = "http://www.chinatelecom.com.cn/corp/index.html"
linet_1.GetURL(ls_url , lir_data)
lblb_htm  = of_GBK2BIG5(lir_data.ib_data)
ls_htm  = String(lblb_htm , EncodingANSI!)

destroy linet
destroy lir_data

8 個回應:

匿名 提到...

您好,
我依照您的code,在PB11.5進行實作,但一直試不出來.
我的OS是Window XP SP3,繁體版,
底下是大約的code:
ls_t = "業"
lb_b = blob(ls_t, EncodingANSI!)
lb_r = of_BIG52GBK(lb_b)
ls_r = string(lb_r, EncodingANSI!)

但卻無法正常回傳"業"的簡體,傳回了"珛 ",
請問, 我是否有未注意到的地方呢?
謝謝

WILDOX 提到...

Hello 匿名:
這個問題出在這一行
ls_r = string(lb_r, EncodingANSI!)
知道為什麼函數轉換簡體字進出函數都必須使用blob變數承接嗎?
原因就在您使用string轉譯後存入ls_r1變數時就會進行字元編碼對照轉換了,所以你永遠看不到正確的「簡體字」。使用blob變數PB的VM不會對此種變數進行「字元編碼對照」,因此整個函數過程只要跟簡體字資料有關的都是存在BLOB變數處理的。
由於PB的VM這個「好心」的動作,所以您才可以於String變數操作ANSI編碼與UNICODE編碼的中文字,而不用考慮太多,但也因為這個「好心」的動作導致您在使用異種字碼表時產生不必要的轉換。

WILDOX 提到...

Re: 匿名 <4114255658815111286>
給您看一張用Convert-Z轉換的結果
https://lh5.googleusercontent.com/-53HTy6sC2qA/T73PVcLCYrI/AAAAAAAAAgA/WFUERAxEi7s/s600/convert-z-docc.png

分岔路 提到...

大大您好:
小弟目前也是在測試繁體轉簡體部分,也是卡在blob部分,我想請問的是如果無法使用string(lb_r, EncodingANSI!)方式呈顯簡體文字,那要如何呈現簡體文字呢?
謝謝

WILDOX 提到...

Re: 分岔路 <469093337721936543>
PB10的VM使用作業系統的核心編碼方式,所以string會使用作業系統的字碼對照表(想知道作業系統對照表,可以查看[控制台][地區與語言選項][進階]),變更作業系統語言可以讓它正常顯示,但我想你不會這麼做,因為變更作業系統語言可能會導致某些軟體更新不正常(例如office update),如果只是要檢視,我的做法會將該簡體字資料放到一個本地html檔裡面,然後設定html編碼方式,利用PB內嵌Web Browser OLE 瀏覽該html檔案:
假如你的簡體字內容放在lb_r中,前後就加編碼方式lb_html = blob('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=gb2312"></head><pre>') + lb_r + blob('</pre><body></body></html>')
然後儲存lb_html到html檔案,這個檔案用瀏覽器開啟就可以正常檢視內容,當然你也可以把瀏覽器OLE嵌入你的程式,讓它直接開啟就好了。

匿名 提到...

謝謝您的回答,謝謝

匿名 提到...

我覺得PB10以上的版本,只要寫這樣就可以了。
//宣告external functions
Function Long LCMapString(Long Locale,Long dwMapFlags,String lpSrcStr,Long cchSrc,ref String lpDestStr,Long cchDest) LIBRARY "kernel32.dll" Alias For "LCMapStringW;unicode"

//最重要的就是.....Alias For "LCMapStringW;unicode"

//然後
//簡轉繁
string ls_out,ls_in
long ll_in_len

ls_in = as_big5
ll_in_len = len(ls_in)*2+1
ls_out = space(ll_in_len)

LCMapString(2052,LCMAP_SIMPLIFIED_CHINESE, ls_in,-1, ls_out, ll_in_len)

Return ls_out

//繁轉簡
string ls_in,ls_out
long ll_in_len

ls_in = as_gbk
ll_in_len = len(ls_in)*2+1
ls_out = space(ll_in_len)

LCMapString(2052,LCMAP_TRADITIONAL_CHINESE,ls_in,-1,ls_out,ll_in_len)

Return ls_out

匿名 提到...

請問版大
PB8有無提供big-5 轉utf8 的function ?
(目前GOOLE不到資料)
若無提供,我要如何將big-5轉成uft8??