最近在用VC6做資訊抓取方面的一些工作,前兩天初步研究了Regex,很方便的從網上把資訊抓取下來了,後台儲存用了Mysql資料庫,主要也是為了以後Web開發方便。不過,Mysql在使用的過程中編碼方面遇到了一些問題,這裡記錄下來,為自己也為可能遇到這方面問題的朋友提供參考。
中文網頁編碼現在主要是Gb2132和Utf8比較多,從網上抓取網頁的時候,Gb2132在VC6裡不用做轉化,因為VC6預設的就是多位元組儲存,不會遇到亂碼的問題,但如果網頁是Utf8編碼,則抓取的結果就是亂碼,需要將utf8轉到多位元組以便在VC內處理。如可以按下面轉化一下:
//////////////////////////
int n = MultiByteToWideChar(CP_UTF8,0,strData,strData.GetLength(),NULL,0);
WCHAR *pChar = new WCHAR[n+1];
MultiByteToWideChar(CP_UTF8,0, strData, strData.GetLength(),pChar,n);
pChar[n] = 0;
char szANSI[1024];
WideCharToMultiByte ( CP_ACP, WC_COMPOSITECHECK, pChar, -1, szANSI, sizeof(szANSI), NULL, NULL );
///////////////////////////
szANSI就是可以使用的多位元組字串了。
在對VC6內部對字串處理完後,儲存到Mysql資料庫時也會遇到編碼的問題。
需要說明的是,如果儲存的時候不根據資料庫的編碼設定進行相應的編碼處理,雖然資料能存入資料庫,但取的時候就會出問題了,大部分情況下你會看到一對亂碼,而不知道改如何處理。
所以,在這裡我們設定資料庫的儲存編碼格式為Utf8(主要是應用方便,也可以是別的,如Gb3132等),這樣建立表的時候最好顯式說明一下。
//////////////////////////
create table if not exists xxx(id int(4) not null primary key auto_increment, ...) default charset = utf8;
串連資料庫時,在mysql_init(&g_mysql)之後,設定一下讀寫編碼:
mysql_query( &g_mysql, _T("set names 'utf8'")
//////////////////////////
這樣,準備工作做完了,儲存的時候就是編碼轉換的問題了。因為VC6裡字串是多位元組編碼(其實應該就是gb2132),儲存的時候Mysql資料庫是Utf8編碼,如果不轉換,插入時就會出錯,給出字元不能識別的錯誤提示。
在這裡, 我們需要一個字元編碼轉換函式,參考http://www.vckbase.com/document/viewdoc/?id=1444裡面的轉換函式GB2312ToUTF_8:
//////////////////////////
//GB2312 轉為 UTF-8
char* GB2312ToUTF_8(char *pText, int pLen)
{
int nULen = 1 + pLen*2;//pLen + (pLen >> 2) + 2;
char buf[4];
char* rst = new char[nULen];
memset(buf,0,4);
memset(rst,0,nULen);
int i = 0;
int j = 0;
while(i < pLen)
{
//如果是英文直接複製就可以
if( *(pText + i) >= 0)
{
rst[j++] = pText[i++];
}
else
{
WCHAR pbuffer;
Gb2312ToUnicode(&pbuffer,pText+i);
UnicodeToUTF_8(buf,&pbuffer);
unsigned short int tmp = 0;
tmp = rst[j] = buf[0];
tmp = rst[j+1] = buf[1];
tmp = rst[j+2] = buf[2];
j += 3;
i += 2;
}
}
rst[j] = '\0';
return rst;
}
注意原文中在字串長度計算時有點小問題,上面改了一下,返回開闢記憶體的指標,用完後記得釋放記憶體;
對了,用到的幾個函數,如下:
// 把UTF-8轉換成Unicode
void 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 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;
}
// 把Unicode 轉換成 GB2312
void UnicodeToGB2312(char* pOut,unsigned short uData)
{
WideCharToMultiByte(CP_ACP,NULL,&uData,1,pOut,sizeof(WCHAR),NULL,NULL);
return;
}
// GB2312 轉換成 Unicode
void Gb2312ToUnicode(WCHAR* pOut,char *gbBuffer)
{
::MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,gbBuffer,2,pOut,1);
return;
}
//////////////////////////
字串經過上面的編碼轉換就可以存入到utf8編碼的Mysql資料庫了
strSQL.Format("insert into xxx(name, ...) values('%s', ...)", szName...);
if( mysql_query(&g_mysql, strSQL)!=0 )
{
cout<< mysql_error(&g_mysql) << endl;
errorFile.WriteString(strSQL);
continue;
}
delete[] szName;
/////////////////////////////////////////
不過上述代碼在實際使用中我還遇到了一個問題,字串中存在以下utf8字元時會導致SQL執行失敗,
\xE0\x84\x81
\xE0\x82\xB7
\xE0\x80\xBF
\xE0\x90\x96
\xE0\x8B\x8A
初步想法是替換掉這些字元,如果有更好的辦法,歡迎進一步交流探討。
--------------------------------------------
ppzhang | giszhang@gmail.com | 2009-05-27