原作者: 不詳
加註 : 蕭沖

/*
蕭沖 於20051017注記
從下面的程式可以猜測出

1/ GBK字集裡含有繁體字型與簡體字型
2/ GBK裡的繁體字型部份轉為Unicode後便與Big5轉為Unicode後的值是一樣的
3/ 使用LCMapString函數可將GBK裡的繁與簡字體做內碼映射轉換
4/ 常用之GB2312內碼其實是GBK字集裡的子集合(即GBK包含GB2312)
5/ GBK裡有繁體字,簡體字,日文漢字,韓國漢字等。GBK是「國(G)家標(B)準擴(K)展」的漢語拼音字首

由以上的三個重點演出如下的logicl

1/ Big5 to GB 方法為:
Big5->Unicode (使用MultiByteToWideChar)
->GBK繁字範圍->GBK簡字範圍(相當於GB2312) (使用LCMapString)

2/ GB to Big5 方法為:
GBk簡字範圍(相當於GB2312)->GBK繁字範圍字集 (使用LCMapString)
->Unicode->Big5 (使用WideCharToMultiByte)
*/
//1. 輸入Big5字符,返回Gb簡體字符
//---------------------------------------------------------------------------
//函數輸入Big5字符,返回Gb簡體字符
//---------------------------------------------------------------------------
AnsiString __fastcall Big2Gb(AnsiString sBig)
{
char* pszBig5=NULL; //Big5編碼的字符
wchar_t* wszUnicode=NULL; //Unicode編碼的字符
char* pszGbt=NULL; //Gb編碼的繁體字符
char* pszGbs=NULL; //Gb編碼的簡體字符
AnsiString sGb; //返回的字符串
int iLen=0; //需要轉換的字符數

pszBig5=sBig.c_str(); //讀入需要轉換的字符參數

//計算轉換的字符數
iLen=MultiByteToWideChar (950, 0, pszBig5, -1, NULL,0) ;
//給wszUnicode分配內存
wszUnicode=new wchar_t[iLen+1];
//轉換Big5碼到Unicode碼,使用了API函數MultiByteToWideChar
MultiByteToWideChar (950, 0, pszBig5, -1, wszUnicode,iLen);

//計算轉換的字符數
iLen=WideCharToMultiByte (936, 0, (PWSTR) wszUnicode, -1, NULL,0, NULL, NULL) ;
//給pszGbt分配內存
pszGbt=new char[iLen+1];
//給pszGbs分配內存
pszGbs=new char[iLen+1];
//轉換Unicode碼到Gb碼繁體,使用API函數WideCharToMultiByte
WideCharToMultiByte (936, 0, (PWSTR) wszUnicode, -1, pszGbt,iLen, NULL, NULL) ;

//轉換Gb碼繁體到Gb碼簡體,使用API函數LCMapString
LCMapString(0x0804,LCMAP_SIMPLIFIED_CHINESE, pszGbt, -1, pszGbs, iLen);

//返回Gb碼簡體字符
sGb=pszGbs;

//釋放內存
delete [] wszUnicode;
delete [] pszGbt;
delete [] pszGbs;

return sGb;
}

//2. 輸入Gb字符,返回Big5字符
//---------------------------------------------------------------------------
//函數輸入Gb字符,返回Big5字符
//---------------------------------------------------------------------------
AnsiString __fastcall Gb2Big(AnsiString sGb)
{
char* pszGbt=NULL; //Gb編碼的繁體字符
char* pszGbs=NULL; //Gb編碼的簡體字符
wchar_t* wszUnicode=NULL; //Unicode編碼的字符
char* pszBig5=NULL; //Big5編碼的字符
AnsiString sBig5; //返回的字符串
int iLen=0; //需要轉換的字符數


pszGbs=sGb.c_str(); //讀入需要轉換的字符參數

//計算轉換的字符數
iLen=MultiByteToWideChar (936, 0, pszGbs, -1, NULL,0) ;

//給pszGbt分配內存
pszGbt=new char[iLen*2+1];
//轉換Gb碼簡體到Gb碼繁體,使用API函數LCMapString
LCMapString(0x0804,LCMAP_TRADITIONAL_CHINESE, pszGbs, -1, pszGbt, iLen*2);

//給wszUnicode分配內存
wszUnicode=new wchar_t[iLen+1];
//轉換Gb碼到Unicode碼,使用了API函數MultiByteToWideChar
MultiByteToWideChar (936, 0, pszGbt, -1, wszUnicode,iLen);

//begin 蕭沖加改某些符號無法互轉,先手動取代!
const wchar_t wt = 0x2506;

for(int i=0;i <= iLen;i++)
{ if(wszUnicode[i]==wt)
    { wszUnicode[i]=0x2502; }
}
//end 蕭沖修正
//計算轉換的字符數
iLen=WideCharToMultiByte (950, 0, (PWSTR) wszUnicode, -1, NULL,0, NULL, NULL) ;
//給pszBig5分配內存
pszBig5=new char[iLen+1];
//轉換Unicode碼到Big5碼,使用API函數WideCharToMultiByte
WideCharToMultiByte (950, 0, (PWSTR) wszUnicode, -1, pszBig5,iLen, NULL, NULL) ;
//返回Big5碼字符
sBig5=pszBig5;
//釋放內存
delete [] wszUnicode;
delete [] pszGbt;
delete [] pszBig5;
return sBig5;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
     this->Edit3->Text = Gb2Big(this->Edit1->Text);
}
創作者介紹
創作者 aftcast 的頭像
aftcast

蕭沖的書房

aftcast 發表在 痞客邦 留言(1) 人氣()


留言列表 (1)

發表留言
  • hi
  • wprintf 與 fwprintf 缺陷

    #include <stdio.h>
    #include <locale.h>

    //由於 source code 可以 L"" 作出 unicode 編碼, 故 source code 檔案本身是 unicode 方式儲存而不是 ANSI! 否則 source
    //code 將無法正確儲存非當地 locale 的字元, 如此在編譯階段才能作出 "" 的 MBCS 編碼與 L"" 的 unicode 編碼.

    int _tmain(int argc, _TCHAR* argv[])
    {
    setlocale(LC_ALL,"");

    /* ex1
    執行行時期 wprintf 內部將 unicode -> MBCS, 每個 unicode 字元都可以轉換成與當地 locale 相對 MBCS.
    */
    WCHAR *s1=L"台灣";
    wprintf(L"%s\n",s1); //台灣

    /* ex2
    執行時期 wprintf 內部將 unicode -> MBCS, 通常只要來源字串是 unicode 編碼就可以暢行無阻,
    但是有也會有例外的, 假若來源字串是 unicode 編碼, 但是它轉成 MBCS 後超出當地的 locale,
    那這個 unicode 編碼的字元無法對應到當地 locale 的 MBCS, 內部會將無法轉換成 MBCS 的 unicode
    字元給編碼成 MBCS 的 '?' 字元, 故在執行時期 门、学、级、编、书 才變成 '?' 字元.
    */
    WCHAR* s2=L"入门到学懂高级编程书集"; //unicode 編碼, 但是簡體部分超出 codepage 950 的 MBCS 轉換範圍
    wprintf(L"%s\n",s2); //入?到?懂高??程?集

    /* ex3
    編譯時期編譯器對 "" 作出 MBCS 編碼時無法找到 门、学、级、编、书 這些與當地 locale 對應的 MBCS,
    故在編譯階段 门、学、级、编、书 就已經變成 '?' 字元. vs2005 會出現如下編譯警告:
    warning C4566: character represented by universal-character-name '\u95E8' cannot be represented in the current code page (950)
    warning C4566: character represented by universal-character-name '\u5B66' cannot be represented in the current code page (950)
    warning C4566: character represented by universal-character-name '\u7EA7' cannot be represented in the current code page (950)
    warning C4566: character represented by universal-character-name '\u7F16' cannot be represented in the current code page (950)
    warning C4566: character represented by universal-character-name '\u4E66' cannot be represented in the current code page (950)
    */
    char *s3="入门到学懂高级编程书集";
    printf("%s\n",s3); //入?到?懂高??程?集
    return 0;

    /*
    總結:
    從 ex2 和 ex3 的輸出上來說, ex2 是在執行時期 wprintf 內部做出轉換; 然而 ex3 是編譯時期編譯器作出轉換!
    然而 wprintf 和我們所期待有所落差, 它是在執行時期由 unicode -> MBCS, 這個過程涉及到 2 部分:

    1 -> ASCII code, unicode 字元屬於 ASCII code 那麼內部將自動轉成 ANSI, 這是因為所有語系的 MBCS 都包含 ASCII code.
    2 -> non-ASCII code, unicode 字元不屬於 ASCII code 那麼內部將無法轉成與當地 locale 對應的 MBCS, 因此在 wprintf
    呼叫前必須明確使用 setlocale, 它不會自動預設為當地 locale 轉換也就是 setlocale(LC_ALL,"")!

    所以使用 wprintf 要注意 2 個地方, 一是使用前要 setlocale, 二是傳入 unicode 編碼字串不能含當地 locale 無法對應的
    unicode 字元, ex2 就是個例子.

    wprintf 與 wfprintf 是同樣道理, 在使用 wprintf 或 wprintf(資料流轉向到file) 寫檔時要注意下列事項:

    1. wprintf 若不呼叫 setlocale(LC_ALL,""), 將視為 unicode 的來源字串, 當內部無法對
    unicode -> MBCS 轉換因為沒設定 locale, 將導致 unicode 會轉成 '?' 的 MBCS 字元.
    NT 上最後 API 還是會將 MBCS(含有無法正確轉換的 ? 字元) 轉成 UNICODE 輸出. 同理,
    wfprintf 也和 wprintf 運作一樣, 若要真正寫入 unicode 到檔案, 應該是使用 write()
    以二進位方式寫入, 否則雖然來源字串是 unicode, 但是寫入檔案卻是 MBCS.

    2. 開檔為 text 模式時, \r 寫入檔案依然是 \r, 然而 \n 寫入檔案是 \r\n, 若 \r\n 寫檔
    會變成 \r\r\n! 當開啟時會發現並沒有甚麼顯示差異, 但是檔案大小竟是 3 bytes! 不正
    常單一寫入 \r 到檔案中會發生不可預期的顯示錯亂.
    */
    }