真正UTF-8與GB2312間的轉換(兼容windows和Linux)
UTF-8與GB2312間的轉換
作者:曾劉彬
中文轉碼是程序員可能經常遇到的一個問題,鄙人在這方面有些心得,故在此拋磚引玉了。
我在網上看到好多關於UTF-8與BG2312間互相轉換都用一下的方法(爲了方便,以後我稱之爲“拼湊法”):
// 把UTF-8轉換成Unicode
void CChineseCodeLib::UTF_8ToUnicode(WCHAR* pOut,char *pText)
{
char* uchar = (char *)pOut;
uchar[1] = ((pText[0] & 0x0F) << 4) + ((pText[1] >> 2) & 0x0F);
uchar[0] = ((pText[1] & 0x03) << 6) + (pText[2] & 0x3F);
return;
}
// Unicode 轉換成UTF-8
void CChineseCodeLib::UnicodeToUTF_8(char* pOut,WCHAR* pText)
{
// 注意 WCHAR高低字的順序,低字節在前,高字節在後
char* pchar = (char *)pText;
pOut[0] = (0xE0 | ((pchar[1] & 0xF0) >> 4));
pOut[1] = (0x80 | ((pchar[1] & 0x0F) << 2)) + ((pchar[0] & 0xC0) >> 6);
pOut[2] = (0x80 | (pchar[0] & 0x3F));
return;
}
“拼湊法”可以成功的轉換大部分的UTF-8編碼,不過作爲一個負責的人,我想指出其中的缺陷:
真正的UTF-8的編碼規則如下:
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
可見UTF-8 編碼字符理論上可以是1 - 6 個字節長,而“拼湊法”只處理了1字節和3字節兩種編碼方式。有的人可能會說:16 位 BMP 字符最多只用到 3 字節長。這沒錯,拼湊發也沒處理2字節編碼方式。所以我得出結論:“拼湊法”是不安全的。
言歸正傳,其實UTF-8與GB2312之間的轉化不用弄得那麼複雜。
在windows下用這兩個現成的函數,通過不同的參數就能實現。
::WideCharToMultiByte(。。。)
::MultiByteToWideChar(。。。)
若在Linux下,則用iconv命令實現。 具體代碼如下:
如果是windows下,則:#define __cdn_win32_platform__
#define CP_GB2312 20936
class CodingTransformer {
public:
//GB2312 轉爲 UTF-8
int UTF_8ToGB2312(char* pOut, int iBufSize, char *pText, int iLenth);
//GB2312 轉爲 UTF-8
int GB2312ToUTF_8(char* pOut, int iBufSize,char *pText, int iLenth);
};
int CodingTransformer::UTF_8ToGB2312(char* pOut, int iBufSize, char *pText, int pLen)
{
#ifdef __cdn_win32_platform__
WCHAR* pWtemp = new WCHAR[pLen];
int iWcharLenth = ::MultiByteToWideChar(CP_UTF8,0,pText,-1,pWtemp,pLen);
if (0 == iWcharLenth)
{
DWORD dwLastErr = GetLastError();
printf("alexaroma:轉碼錯誤,錯誤號:%d/n", dwLastErr);
delete pWtemp;
return 0;
}
int iMultByteLenth = ::WideCharToMultiByte(CP_GB2312, NULL, pWtemp,
iWcharLenth, pOut,
iBufSize, NULL, NULL);
if (0 == iMultByteLenth)
{
DWORD dwLastErr = GetLastError();
printf("alexaroma:轉碼錯誤,錯誤號:%d/n", dwLastErr);
delete pWtemp;
return 0;
}
delete pWtemp;
return iMultByteLenth;
#else//__cdn_win32_platform__
iconv_t cd;
int rc;
char **pin = &pText;
char **pout = &pOut;
int outlen = iBufSize;
cd = iconv_open("GB2312","UTF-8");
if(cd==0)return -1;
memset(pOut,0,strlen(pOut));
if(iconv(cd,pin,(size_t *)&pLen,pout,(size_t *)&outlen) == -1)
{
iconv_close(cd);
return outlen;
}
iconv_close(cd);
return -1;
#endif//__cdn_win32_platform__
}
int CodingTransformer::GB2312ToUTF_8(char* pOut, int iBufSize,
char *pText, int pLen)
{
#ifdef __cdn_win32_platform__
WCHAR* pWtemp = new WCHAR[pLen];
int iWcharLenth = ::MultiByteToWideChar(CP_GB2312, MB_PRECOMPOSED,
pText, -1, pWtemp, pLen);
if (0 == iWcharLenth)
{
DWORD dwLastErr = GetLastError();
printf("alexaroma:轉碼錯誤,錯誤號:%d/n", dwLastErr);
delete pWtemp; return 0;
}
int iMultByteLenth = ::WideCharToMultiByte(CP_UTF8, 0, pWtemp,
iWcharLenth, pOut,
iBufSize, NULL, NULL);
if (0 == iMultByteLenth)
{
DWORD dwLastErr = GetLastError();
printf("alexaroma:轉碼錯誤,錯誤號:%d/n", dwLastErr);
delete pWtemp;
return 0;
}
delete pWtemp;
return iMultByteLenth;
#else//__cdn_win32_platform__
iconv_t cd;
int rc;
char **pin = &pText;
char **pout = &pOut;
int outlen = iBufSize;
cd=iconv_open("UTF-8","GB2312");
if(cd==0)return -1;
memset(pOut,0,strlen(pOut));
if(iconv(cd,pin,(size_t *)&pLen,pout,(size_t *)&outlen) == -1)
{
iconv_close(cd);
return outlen;
}
iconv_close(cd);
return -1;
#endif//__cdn_win32_platform__
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.