VS下用c++连接mysql,字符编码格式转换
这两天在VS2010下用c++连接mysql数据库,开始的时候能成功读取数据库中的INT型数据,但对其中的varchar类型无法正常读取(数据库采用utf-8编码),读取出来的都是乱码。后来经过在网上搜索和思考,终于解决了。整理如下。
问题解析:mysql中是用utf-8格式存储字符串,而VS中是用ANSI(文件-高级保存选项,可以看到,中文操作系统的默认代码页为GB2312,即ANSI的一个代码页20936),所以在二者之间需要进行转换。核心是利用两个函数:MultiByteToWideChar和WideCharToMultiByte
参照博文http://blog.csdn.net/waden/article/details/6125471和博文http://www.cnblogs.com/wind-net/archive/2012/10/31/2718329.html,代码如下:
#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类型里面存的内容就已经是乱码了,后面不管怎么转换都没用。博文http://www.cnblogs.com/wind-net/archive/2012/10/31/2718329.html里面就犯了这样的错误。同理,函数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里申请的。