在 Powerbuilder 下轉換 UTF-8 文字為 ANSI 文字


PB10 + 版本 & Pocket Powerbuilder 2.1+ 版本
直接使用 String 就可以轉換

lbl_src = blob( ls_string1 , encodingutf8! )
ls_string2 = string( lbl_src , encodingansi!)


powerbuilder 9 (含以下版本) 就得借助 Windows API 來完成轉換

首先,你必須宣告外部函數(declare external functions)

function ulong MultiByteToWideChar(
         ulong CodePage,
         ulong dwflags,
         ref string lpmultibytestr,
         ulong cchmultibyte,
         ref blob lpwidecharstr,
         ulong cchwidechar
) library "kernel32.dll"

function ulong WideCharToMultiByte(
         ulong CodePage,
         ulong dwFlags,
         ref blob lpWideCharStr,
         ulong cchWideChar,
         ref string lpMultiByteStr,
         ulong cbMultiByte,
         ref string lpUsedDefaultChar,
         ref boolean lpUsedDefaultChar
) library "kernel32.dll"


然後下面實做一個轉換函數

string of_utf8_to_ansi(string as_utf8)


程式碼:
//轉換 utf-8 -> ansi
//use a wide-char native string as pivot
constant ulong CP_ACP = 0
constant ulong CP_UTF8 = 65001 //UTF8的PAGE CODE

string ls_wide, ls_ansi, ls_null
blob lbl_wide
ulong ul_len
boolean lb_flag

setnull(ls_null)
lb_flag = false

//取得 utf-8 轉換 wide-char 後的字串長度
setnull(lbl_wide)
ul_len = multibytetowidechar(CP_UTF8, 0, as_utf8, -1, lbl_wide, 0)
//配置記憶體空間給字串
ls_wide = space(ul_len * 2)
lbl_wide = blob(ls_wide)
//轉換 utf-8 -> wide char
ul_len = multibytetowidechar(CP_UTF8, 0, as_utf8, -1, lbl_wide, ul_len)
//取得ANSI字串長度
setnull(ls_ansi)
ul_len = widechartomultibyte(CP_ACP, 0, lbl_wide, -1, ls_ansi, 0, ls_null, lb_flag)
//配置足夠空間給 windows 寫入
ls_ansi = space(ul_len)
//轉換 wide-char -> ansi
ul_len = widechartomultibyte(CP_ACP, 0, lbl_wide, -1, ls_ansi, ul_len, ls_null, lb_flag)

return ls_ansi


下面利用 inet.GetUrl 去取得 API 內容,這個內容是 UTF-8 編碼的,所以就用 of_utf_to_ansi 去變更編碼:

先建立一個 internetresult 物件,名為 n_internetresult


然後在 n_internetresult 上建立 of_utf8 to_ansi 函數(function),把上面代碼複製進去


再來建立 instance variables

String is_data

最後編輯 internetdata 內建函數(function)


內容:

Blob lblb_data
lblb_data = data
is_data = of_utf8_to_ansi(string(iblb_data))
Return 1


之後就可以在需要的地方呼叫使用

n_internetresult i_ir
inet i_net
String ls_empl , ls_url , ls_name
Integer li_ret

i_ir = Create n_internetresult
i_net = Create inet

ls_empl = "0099685153"
ls_url = "http://mysite.com/api/GetEmployeeName.cfm?no=" + ls_empl

li_ret = i_net.GetUrl(ls_url , i_ir)
if li_ret < 0 then
//網路錯誤
messagebox("result" , "Network Error !")
else
ls_name = i_ir.is_data
messagebox("result" , "Employee Name is " + ls_name)
end if

Destroy inet
Destroy i_ir



參考 https://stackoverflow.com/questions/22336948/powerbuilder-importfile-of-utf-8-converting-utf-8-to-ansi

留言

magicdog20寫道…
以-1代替字符串长度可能有bug,因为以blob方式存放的字符串结尾并不一定有null,特别是从unicode转ansi的时候,这些unicode可能是从别的语言传入的。

实测用这种方式转字符串非常慢,比系统提供的FromUnicode和ToUnicode还慢。建议改用adostream的方法,使用简单,效率还高。
比如从unicode转ansi

OLEObject ole_ADOStream
ole_ADOStream = CREATE OLEObject
li_rtn = ole_ADOStream.ConnectToNewObject("ADODB.Stream")
ole_ADOStream.Charset = "unicode"
ole_adostream.Type = 1 //二进制数据Binary
ole_ADOStream.Open()
ole_ADOStream.Write(lbl_unicode)
ole_ADOStream.Position = 0
ole_ADOStream.Type = 2 //文本数据
ls_ansi = ole_ADOStream.readtext()
ole_ADOStream.Close()
destroy ole_ADOStream

注意lbl_unicode头部要有说明是big endian还是litte endian的标记,big endian是FEFF,little enddian是FFFE。
WILDOX寫道…
Re: magicdog20 <6305239174358330567>

感謝提供另一種編碼方式,我不會用系统提供的FromUnicode和ToUnicode主要是powerbuilder6不支援,另外它也無法轉utf-16以外的編碼,所以不實用。我主要是解析uft-8文件。

確實,Byte Length使用 -1 是讓API自動偵測長度,是有可能遇到問題的,那是因為基於我使用的環境所以才如此設計。

如果,使用您的方法轉 UTF-8,程式碼將如下:
OLEObject ole_ADOStream
ole_ADOStream = CREATE OLEObject
li_rtn = ole_ADOStream.ConnectToNewObject("ADODB.Stream")
ole_ADOStream.Charset = "utf-8" //來源編碼方式
ole_adostream.Type = 1 //二进制数据Binary
ole_ADOStream.Open()
ole_ADOStream.Write(lbl_unicode)
ole_ADOStream.Position = 0
ole_ADOStream.Type = 2 //文本数据
ls_ansi = ole_ADOStream.readtext()
ole_ADOStream.Close()
destroy ole_ADOStream

這個網誌中的熱門文章

【研究】列印的條碼為什麼很難刷(掃描)

統一發票列印小程式

C# 使用 Process.Start 執行外部程式