這兩天在學(xué)習(xí)使用jni,在java程序中,調(diào)用海量詞典的dll。利用jni的GetStringChars函數(shù)和NewString函數(shù)時(shí),遇到了中文亂碼的問(wèn)題,折騰了一個(gè)晚上。查閱了一些資料,總結(jié)如下:
一.相關(guān)概念
- java內(nèi)部是使用16bit的unicode編碼(UTF-16)來(lái)表示字符串的,無(wú)論中文英文都是2字節(jié);
- jni內(nèi)部是使用UTF-8編碼來(lái)表示字符串的,UTF-8是變長(zhǎng)編碼的unicode,一般ascii字符是1字節(jié),中文是3字節(jié);
- c/c++使用的是原始數(shù)據(jù),ascii就是一個(gè)字節(jié)了,中文一般是GB2312編碼,用兩個(gè)字節(jié)來(lái)表示一個(gè)漢字。
明確了概念,操作就比較清楚了。下面根據(jù)字符流的方向來(lái)分別說(shuō)明一下
1、java --> c/c++
這種情況中,java調(diào)用的時(shí)候使用的是UTF-16編碼的字符串,jvm把這個(gè)字符串傳給jni,c/c++得到的輸入是jstring,這個(gè)時(shí)候,可以利用jni提供的兩種函數(shù),一個(gè)是GetStringUTFChars,這個(gè)函數(shù)將得到一個(gè)UTF-8編碼的字符串;另一個(gè)是 GetStringChars這個(gè)將得到UTF-16編碼的字符串。無(wú)論那個(gè)函數(shù),得到的字符串如果含有中文,都需要進(jìn)一步轉(zhuǎn)化成GB2312的編碼。示意圖如下:
String
(UTF-16)
|
[java] |
-------------------- JNI 調(diào)用
[cpp] |
v
jstring
(UTF-16)
|
+--------+---------+
|GetStringChars |GetStringUTFChars
| |
v v
wchar_t* char*
(UTF_16) (UTF-8)
2、c/c++ --> java
jni返回給java的字符串,c/c++首先應(yīng)該負(fù)責(zé)把這個(gè)字符串變成UTF-8或者UTF-16格式,然后通過(guò)NewStringUTF或者NewString來(lái)把它封裝成jstring,返回給java就可以了。
String
(UTF-16)
^
|
[java] |
-------------------- JNI 返回
[cpp] |
jstring
(UTF-16)
^
|
+--------+---------+
^ ^
| |
|NewString |NewStringUTF
wchar_t* char*
(UTF_16) (UTF-8)
如果字符串中不含中文字符,只是標(biāo)準(zhǔn)的ascii碼,那么使用GetStringUTFChars/NewStringUTF就可以搞定了,因?yàn)檫@種情況下,UTF-8編碼和ascii編碼是一致的,不需要轉(zhuǎn)換。
但是如果字符串中有中文字符,那么在c/c++部分進(jìn)行編碼轉(zhuǎn)換就是一個(gè)必須了。我們需要兩個(gè)轉(zhuǎn)換函數(shù),一個(gè)是把UTF8/16的編碼轉(zhuǎn)成GB2312;一個(gè)是把GB2312轉(zhuǎn)成UTF8/16。
這里要說(shuō)明一下:linux和win32都支持wchar,這個(gè)事實(shí)上就是寬度為16bit的unicode編碼UTF16,所以,如果我們的 c/c++程序中完全使用wchar類(lèi)型,那么理論上是不需要這種轉(zhuǎn)換的。但是實(shí)際上,我們不可能完全用wchar來(lái)取代char的,所以就目前大多數(shù)應(yīng)用而言,轉(zhuǎn)換仍然是必須的。
二。一種轉(zhuǎn)換方法
使用wide char類(lèi)型來(lái)轉(zhuǎn)換。
char* jstringToWindows( JNIEnv *env, jstring jstr )
{ //UTF8/16轉(zhuǎn)換成gb2312
int length = (env)->GetStringLength(jstr );
const jchar* jcstr = (env)->GetStringChars(jstr, 0 );
char* rtn = (char*)malloc( length*2+1 );
int size = 0;
size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL );
if( size <= 0 )
return NULL;
(env)->ReleaseStringChars(jstr, jcstr );
rtn[size] = 0;
return rtn;
}
jstring WindowsTojstring( JNIEnv* env, const char* str )
{//gb2312轉(zhuǎn)換成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;
}