关于c++与java中文乱码问题分析与解决
DionysosLai([email protected]) 2014/8/1
问题分析:
之所以会出现中文乱码问题,归根结底在于中文的编码与英文的编码方式存在差异。
在java内部是使用16bit的unicode编码(即utf-16)来表示字符串,无论英文还是中文都是2字节。
C/C++使用的是原始数据,ascii是一个字节,中文一般是GB2312编码,用2个字节表示一个汉字。
Jni内部是使用utf-8编码表示字符串的,utf-8是扁长的unicode,一般ascii是1字节,中文是3字节。
下面给出在不同字符集编码下的字节数:
英文字母:
字节数 : 1;编码:GB2312 字节数: 1;编码:GBK 字节数 : 1;编码:GB18030
字节数 : 1;编码:ISO-8859-1 字节数: 1;编码:UTF-8 字节数 : 4;编码:UTF-16
字节数 : 2;编码:UTF-16BE 字节数: 2;编码:UTF-16LE
中文汉字:
字节数 : 2;编码:GB2312 字节数: 2;编码:GBK 字节数 : 2;编码:GB18030
字节数 : 1;编码:ISO-8859-1 字节数: 3;编码:UTF-8 字节数 : 4;编码:UTF-16
字节数 : 2;编码:UTF-16BE 字节数: 2;编码:UTF-16LE
二者数据传递分析:
Java->C++数据传递
有上文可知,java使用的是utf-16编码,jvm把数据传递给jni,c++dedao的是输入参数u是jstring。此时,可以利用jni提供的两种函数,一个是GetStringUTFChars,这个函数将得到一个UTF-8编码的字符串;另一个是GetStringChars这个将得到UTF-16编码的字符串。无论那个函数,如果字符串包含中文,都需要进一步转化成C\C++的编码格式:GB2312。
C++->java数据传递
C\C++的英文编码方式是ascii码,对于中文则是GB2312。Jni返回给java的字符串,c\c++首先应该负责把这个字符串扁长utf-8或者utf-16格式,然后通过NewStringUTF或者NewString将字符串封装成jstring,返回给java即可。
如果没有中文字符,就只有标准的ascii码值,那么使用GetStringUTFChars/NewStringUTF可以轻松搞定。因为在这种情况下,utf-8编码和ascii编码是一致,不需要转换。
如果存在中文字符,那么在C\C++部分进行编码就是必须的,一个是把utf-8/16的编码转成GB2312,一个是把GB2312转成utf-8/16。
问题解决:
终于到解决问题的时刻了。(我的大斧已经饥渴难耐了)。
对于编码转换,推荐使用iconv库,iconv库是一个免费独立的编码转化库,支持多种平台,多种编码,而且其行为任何不受外部环境影响。使用iconv库的另外一个好处就是cocos2dx在win32平台下已经集成了其库环境,不需要开发者额外添加,但是在Android平台就需要开发者自己添加。至于如何在Android平台上添加iconv库,在下面会详细讲解(也可以看网上教程,基本方法都是如何在Eclipse上添加库)。
下面给出如何将中文编码格式GB2312,转化成utf-8编码方式。
int CCDirector::GBKToUTF8(std::string &gbkStr) { iconv_t iconvH; iconvH = iconv_open("utf-8","gb2312"); if(iconvH == 0){ return -1; } const char* strChar = gbkStr.c_str(); const char** pin = &strChar; size_t strLength = gbkStr.length(); char* outbuf = (char*)malloc(strLength*4); char* pBuff = outbuf; memset(outbuf,0,strLength*4); size_t outLength = strLength*4; <span style="color:#ff6666;"><strong>#if(CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) if(-1 == iconv(iconvH,pin,&strLength,&outbuf,&outLength)){ iconv_close(iconvH); return -1; } #else if(-1 == iconv(iconvH,(char **)pin,&strLength,&outbuf,&outLength)){ iconv_close(iconvH); return -1; } #endif</strong></span> gbkStr = pBuff; iconv_close(iconvH); return 0; }
注意红色字体代码,在win32平台和android平台传递的参数是不一样的。这个主要原因是由于pin参数是constchar**型,在java中,没有这种类型参数,因此要强制装换成char**类型。
在Eclipse中添加iconv库
Iconv库下载地址如下:
Cocos2dx引擎在win32平台,本身自带了iconv库,库地址在E:\EngilshLogicGame\EnglishGame\cocos2d-x-2.2.3\cocos2dx\platform\third_party\win32,在这里面还包括其他的一些库文件,比方说网络库,解析png库等。引擎在Android平台,没有自带iconv库(个人猜测原因是由于ndk自带iconv库了,因此其实可以不用自己下载iconv库,但这里,我们给出方法是使用自己下载的iconv库),iconv库的下载地址上文已经给出。
通过更改工程proj.android/jni/Android.mk文件,将iconv库文件加载进Eclipse中。在mk文件中加入了下面两句话,如图所示:
注意:这里我将下载的iconv库文件放在了引擎的external文件下(当然,也可以发在别的地方,只需要更改对应的目录文件即可),如图所示:
下一步,是要更改我们下载的iconv库文件中的mk文件(如果是从我这下载的iconv库,我已经更改好了)。
更改方式,如图所示:
注意:画圈的两个关键字,与之前修改的proj.android/jni/Android.mk文件中的关键字是一致的。
到这里,iconv库在Android平台就添加完毕了。
在之前给出的函数GBKToUTF8中,用到了iconv库函数,因此在文件中要包含iconv头文件,具体代码如下所示:
#if(CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) #include "iconv.h" #else #include "../external/libiconv/include/iconv.h" #endif
至此,关于“关于c++与java中文乱码问题分析与解决”话题,就到此为止,希望能对大家有点帮助。也感谢公司的同事给予的帮助,和网上的一些博主。
关于c++与java中文乱码问题分析与解决,布布扣,bubuko.com