最近在用VC6做信息抓取方面的一些工作,前两天初步研究了正则表达式,很方便的从网上把信息抓取下来了,后台存储用了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 | [email protected] | 2009-05-27