【JNI】Java與C++中文字串的傳遞
(2013-01-05 21:33:21)JNI和C++通信中文亂碼的問題
http://developer.51cto.com/art/201112/310128.htm
http://blog.sina.com.cn/s/blog_a000da9d010185hr.html
首先,需要明確幾個關於字符編碼的基本概念:
◆ java內部是使用的16bit的unicode編碼(utf-16)來表示字符串的,無論英文還是中文都是2字節;
◆ jni內部是使用utf-8編碼來表示字符串的,utf-8是變長編碼的unicode,一般ascii字符是1字節,中文是3字節;
◆ c/c++使用的是原始數據,ascii就是一個字節,中文一般是GB2312編碼,用2個字節表示一個漢字。
jni的中文字符串處理
先從字符流的方向分別對java-->C++和C++-->java進行分析
◆ java-->C++
這種情況下,java調用的時候使用的是utf-16編碼的字符串,jvm把這個參數傳遞給jni,C++得到的輸入是jstring,此時,可以利用jni提供的兩種函數,一個是GetStringUTFChars,這個函數將得到一個UTF-8編碼的字符串;另一個是GetStringChars這個將得到UTF-16編碼的字符串。無論那個函數,得到的字符串如果含有中文,都需要進一步轉化成GB2312的編碼。
◆ c/c++ –> java
jni返回給java的字符串,c/c++首先應該負責把這個字符串變成UTF-8或者UTF-16格式,然後通過NewStringUTF或者NewString來把它封裝成jstring,返回給java就可以了。
如果字符串中不含中文字符,只是標準的ascii碼,那麼用GetStringUTFChars/NewStringUTF就可以搞定了,因爲這種情況下,UTF-8編碼和ascii編碼是一致的,不需要轉換。
但是如果字符串中有中文字符,那麼在c/c++部分進行編碼轉換就是一個必須了。我們需要兩個轉換函數,一個是把UTF8/16的編碼轉成GB2312;一個是把GB2312轉成UTF8/16。
下面是一個測試DEMO,native代碼( NDK samples下hellojni ) : (注意紅色部分代碼及API)
#include <string.h>
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <android/log.h>
#include <jni.h>
#define LOG_TAG "hellojni_native"
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
//Java字符串的類和獲取方法ID
jclass gStringClass;
jmethodID gmidStringInit;
jmethodID gmidStringGetBytes;
int isASCII(const char * chp)
{
char ch;
jboolean flag= 1;
while(ch = *chp++){
if(ch & 0x80){
flag = 0;
break;
}
}
return flag;
}
// jstring to char*
char* JstringToPchar(JNIEnv* env, jstring jstr, const char * encoding)
{
char* rtn = NULL;
jstring jencoding;
jencoding=(*env)->NewStringUTF(env,encoding);
jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr, gmidStringGetBytes, jencoding);
jsize alen = (*env)->GetArrayLength(env,barr);
jbyte* ba = (*env)->GetByteArrayElements(env,barr, JNI_FALSE);
if (alen > 0)
{
//LOGI("alen = %d ",alen);
rtn = (char*)malloc( alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env,barr, ba, 0);
return rtn;
}
//char* to jstring
jstring PcharToJstring(JNIEnv* env, const char* pchar,const char * encoding)
{
jstring jencoding;
jbyteArray bytes = (*env)->NewByteArray(env,strlen(pchar));
(*env)->SetByteArrayRegion(env,bytes, 0, strlen(pchar), (jbyte*)pchar);
jencoding = (*env)->NewStringUTF(env,encoding);
return (jstring)(*env)->NewObject(env,gStringClass, gmidStringInit, bytes, jencoding);
}
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,jobject thiz )
{
jstring result;
gStringClass= (*env)->FindClass(env,"java/lang/String");
gmidStringGetBytes= (*env)->GetMethodID(env,gStringClass, "getBytes", "(Ljava/lang/String;)[B");
gmidStringInit= (*env)->GetMethodID(env,gStringClass, "", "([BLjava/lang/String;)V");
//char *myTest = "Hello from JNI !";
char *myTest = "[華仔]Hello from JNI !";
if(isASCII(myTest)) {
LOGI("Is ASCII");
result = (*env)->NewStringUTF(env, myTest);
}
else{
LOGI("Is Not ASCII");
result = PcharToJstring(env,myTest,"gbk");
char *outbuf;
outbuf = JstringToPchar(env,result,"utf-8");
//LOGI("strlen(outbuf) = %d",strlen(outbuf));
//int i;
//for(i = 0 ; i < strlen(outbuf);i++)
// LOGI("outbuf[%d] = %x",i,outbuf[i]);
result = (*env)->NewStringUTF(env, outbuf);
if(outbuf){
free(outbuf);
}
}
return result;
}
測試結果:
===end===