轉自:http://blog.sina.com.cn/fangaosjtu
這兩天在學習使用jni,在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;
}
