在java
程序中,dll
。利用jni
的GetStringChars
函数和NewString
函数时,遇到了中文乱码的问题。查阅了一些资料,总结如下:
一. 相关概念
java
内部是使用16bit
的unicode
编码(UTF-16
)来表示字符串的,无论中文英文都是2
字节;
jni内部是使用UTF-8编码来表示字符串的,UTF-8
是变长编码的unicode
,一般ascii
字符是1
字节,中文是3
字节;
c/c++
使用的是原始数据,ascii
就是一个字节了,中文一般是GB2312
编码,用两个字节来表示一个汉字。
明确了概念,操作就比较清楚了。下面根据字符流的方向来分别说明一下
1、java --> c/c++
这种情况中,java
调用的时候使用的是UTF-16
编码的字符串,jvm
把这个字符串传给jni
,c/c++
得到的输入是jstring
,这个时 候,可以利用jni
提供的两种函数,一个是GetStringUTFChars
,这个函数将得到一个UTF-8
编码的字符串;另一个是 GetStringChars
这个将得到UTF-16
编码的字符串。无论那个函数,得到的字符串如果含有中文,都需要进一步转化成GB2312
的编码。示意图如下:
String
(UTF-16)
|
[java] |
-------------------- JNI 调用
[cpp] |
v
jstring
(UTF-16)
|
+--------+---------+
|GetStringChars |GetStringUTFChars
| |
v v
wchar_t* char*
(UTF_16) (UTF-8)
2、c/c++ --> java
jni
返回给java
的字符串,c/c++
首先应该负责把这个字符串变成UTF-8
或者UTF-16
格式,然后通过NewStringUTF
或者NewString
来把它封装成jstring
,返回给java
就可以了。
String
(UTF-16)
^
|
[java] |
-------------------- JNI 返回
[cpp] |
jstring
(UTF-16)
^
|
+--------+---------+
^ ^
| |
|NewString |NewStringUTF
wchar_t* char*
(UTF_16) (UTF-8)
如果字符串中不含中文字符,只是标准的ascii码,那么使用GetStringUTFChars/NewStringUTF就可以搞定了,因为这种情况下,UTF-8编码和ascii编码是一致的,不需要转换。
但是如果字符串中有中文字符,那么在c/c++部分进行编码转换就是一个必须了。我们需要两个转换函数,一个是把UTF8/16的编码转成GB2312;一个是把GB2312转成UTF8/16。
这里要说明一下:linux和win32都支持wchar,这个事实上就是宽度为16bit的unicode编码UTF16,所以,如果我们的 c/c++程序中完全使用wchar类型,那么理论上是不需要这种转换的。但是实际上,我们不可能完全用wchar来取代char的,所以就目前大多数应 用而言,转换仍然是必须的。
二. 一种转换方法
使用wide char
类型来转换。
char* jstringToWindows( JNIEnv *env, jstring jstr )
{ //UTF8/16转换成gb2312
int length = (env)->GetStringLength(jstr );
const jchar* jcstr = (env)->GetStringChars(jstr, 0 );
int clen = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, NULL,0, NULL, NULL );
char* rtn = (char*)malloc( clen ) //更正。作者原来用的是(char*)malloc( length*2+1 ),当java字符串中同时包含汉字和英文字母时,所需缓冲区大小并不是2倍关系。
int size = 0;
size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,clen, NULL, NULL );
if( size <= 0 )
return NULL;
(env)->ReleaseStringChars(jstr, jcstr );
rtn[size] = 0;
return rtn;
}
jstring WindowsTojstring( JNIEnv* env, const char* str )
{//gb2312转换成utf8/16
jstring rtn = 0;
int slen = strlen(str);
unsigned short * buffer = 0;
if( slen == 0 )
rtn = (env)->NewStringUTF(str );
else
{
int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 );
buffer = (unsigned short *)malloc( length*2 + 1 );
if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) >0 )
rtn = (env)->NewString( (jchar*)buffer, length );
}
if( buffer )
free( buffer );
return rtn;
}
三. REFERENCE
转自:http://blog.sina.com.cn/fangaosjtu