这两天在VS2010下用c++连接mysql数据库,开始的时候能成功读取数据库中的INT型数据,但对其中的varchar类型无法正常读取(数据库采用utf-8编码),读取出来的都是乱码。后来经过在网上搜索和思考,终于解决了。整理如下。
问题解析:mysql中是用utf-8格式存储字符串,而VS中是用ANSI(文件-高级保存选项,可以看到,中文操作系统的默认代码页为GB2312,即ANSI的一个代码页20936),所以在二者之间需要进行转换。核心是利用两个函数:MultiByteToWideChar和WideCharToMultiByte
代码如下:
#include "stdafx.h"
#include <Windows.h>
#include <string>
/*
功能:将UTF8格式的字符转换为ANSI格式
返回值 1:成功
0:失败
*/
bool UTF82ANSI(const char* pIn, std::string& strOut)
{
// UTF8转换为宽字符wchar_t
DWORD dwNum = MultiByteToWideChar(CP_UTF8,NULL ,pIn,-1, NULL, 0);
wchar_t* pUnicode = new wchar_t[dwNum + 1];
memset(pUnicode, 0, dwNum * sizeof(wchar_t));
int ret1 = MultiByteToWideChar(CP_UTF8,NULL ,pIn,-1, pUnicode, dwNum);
//宽字符转换为ANSI
dwNum = WideCharToMultiByte(CP_OEMCP,NULL,pUnicode,-1,NULL,0,NULL,FALSE);
char *pOut = new char[dwNum + 1];
int ret2 = WideCharToMultiByte (CP_OEMCP,NULL,pUnicode,-1,pOut,dwNum,NULL,FALSE);
*(pOut + dwNum) = '\0';
strOut = pOut;
//释放空间
delete[] pUnicode;
pUnicode = NULL;
delete[] pOut;
pOut = NULL;
return ((ret1 != 0) && (ret2 != 0));
}
/*
功能:将ANSI格式的字符转换为UTF8格式
返回值 1:成功
0:失败
*/
bool ANSI2UTF8(const char* pIn, char*& pOut)
{
// ANSI转换为宽字符wchar_t
int len = strlen(pIn);
DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, pIn, len, NULL, 0);
wchar_t* pUnicode = new wchar_t[dwNum+1];
memset(pUnicode, 0, (dwNum+1)*sizeof(wchar_t));
int ret1 = MultiByteToWideChar(CP_ACP, 0, pIn, len, pUnicode, dwNum);
// 宽字符wchar_t转换为UTF8
dwNum = WideCharToMultiByte(CP_UTF8,NULL,pUnicode,-1,NULL,0,NULL,FALSE);
pOut = new char[dwNum + 1];
memset(pOut, 0, sizeof(char)*(dwNum + 1));
int ret2 = WideCharToMultiByte (CP_UTF8,NULL,pUnicode,-1,pOut,dwNum,NULL,FALSE);
*(pOut + dwNum) = '\0';
delete[] pUnicode;
pUnicode = NULL;
return ((ret1 != 0) && (ret2 != 0));
}
核心都是通过宽字符wchar_t作为中间的过渡。
值得指出的是,在函数UTF82ANSI中,欲转换的字符pIn必须是char*型的,而不能是string型的。因为若是string型的,你传入实参的时候就相当于用了ANSI编码方式去识别utf-8编码的字符,结果导致string类型里面存的内容就已经是乱码了,后面不管怎么转换都没用。博文里面就犯了这样的错误。同理,函数ANSI2UTF8中,转换后输出的字符pOut也必须是char*型的,否则若是string型,则系统又默认其为ANSI格式,变为乱码,这样再给mysql数据库用,数据库会试图用utf-8编码方式去识别ANSI格式编码的字符,又会出现错误。
有了上述转换函数后,就可以成功在mysql中插入和读取数据了,之前记得设置连接数据库的编码方式,如下:
con->setClientOption("characterSetResults", "utf8");
之后,就可以读取数据了(从数据库中读取utf-8格式字符,转换为ANSI格式显示):
stmt = con->createStatement();
res = stmt->executeQuery("SELECT * from user");
while (res->next())
{
string name;
if( UTF82ANSI(res->getString("NAME").c_str(), name ) == true)
{
cout<<name<<endl;
}
}
delete res;
delete stmt;
注意,传入参数必须是是char*型的,否则传入时就会变成乱码。
写入数据(将ANSI格式字符写入到utf-8编码的数据库里):
pstmt = con->prepareStatement("INSERT INTO user(ID,NAME) VALUES (?,?)");
string name = "一二三四 五六七八";
char* name_UTF8 = NULL;
if( ANSI2UTF8(name.c_str(), name_UTF8) == true )
{
pstmt->setInt(1,15);
pstmt->setString(2,name_UTF8);
pstmt->executeUpdate();
}
delete[] name_UTF8;
name_UTF8 = NULL;
delete pstmt;
注意:(1)函数传出参数(也即setString的传入参数)必须是char*型的,否则会出现乱码;(2)记得释放name_UTF8的内存,它的内存是在函数ANSI2UTF8里申请的。