未公开的mustang核心秘密(二):jni的返回中文问题

严格的说,我基本没有做过jni的开发,只是修改过bsd和windows下的jdk源码。这些是openjdk的jni部分函数,这函数完全用c写的,幸好不是c++,没有用复杂的设计模式,我还看得懂。
下面这段代码,从控制台通过jni输入字符串,java里面打印
class Prompt {

// native method that prints a prompt and reads a line
private native String getLine(String prompt);

public static void main(String args[]) {
Prompt p = new Prompt();
String input = p.getLine("Type a line: ");
System.out.println("User typed: " + input);
}

static {
System.loadLibrary("jnistudy");
}
}
promt.c的代码
#include <jni.h>
#include <stdio.h>
#include "Prompt.h"

JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const char *str;
str = (*env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL) {
return NULL; /* OutOfMemoryError already thrown */
}
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
/* We assume here that the user does not type more than
* 127 characters */
scanf("%s", buf);
return (*env)->NewStringUTF(env, buf);
}
如果控制台输入中文,java是unicode编码,到jni是utf8编码,输出肯定是乱码。
如果单纯是转化中文,也是很麻烦的,
http://blogger.org.cn/blog/more.asp?name=hongrui&id=25509
使用的办法也是麻烦。
这是jdk用于转码的函数

JNIEXPORT jstring
NewStringPlatform(JNIEnv *env, const char *str)
{
return JNU_NewStringPlatform(env, str);
}

JNIEXPORT jstring JNICALL
JNU_NewStringPlatform(JNIEnv *env, const char *str)
{
jstring result;
jbyteArray hab = 0;
int len;

if (fastEncoding == NO_ENCODING_YET)
initializeEncoding(env);

if ((fastEncoding == FAST_8859_1) || (fastEncoding == NO_ENCODING_YET))
return newString8859_1(env, str);
if (fastEncoding == FAST_646_US)
return newString646_US(env, str);
if (fastEncoding == FAST_CP1252)
return newStringCp1252(env, str);

if ((*env)->EnsureLocalCapacity(env, 2) < 0)
return 0;

len = (int)strlen(str);
hab = (*env)->NewByteArray(env, len);
if (hab != 0) {
(*env)->SetByteArrayRegion(env, hab, 0, len, (jbyte *)str);
if (jnuEncodingSupported(env)) {
result = (*env)->NewObject(env, JNU_ClassString(env),
String_init_ID, hab, jnuEncoding);
} else {
/*If the encoding specified in sun.jnu.encoding is not endorsed
by "Charset.isSupported" we have to fall back to use String(byte[])
explicitly here without specifying the encoding name, in which the
StringCoding class will pickup the iso-8859-1 as the fallback
converter for us.
*/
jmethodID mid = (*env)->GetMethodID(env, JNU_ClassString(env),
"<init>", "([B)V");
result = (*env)->NewObject(env, JNU_ClassString(env), mid, hab);
}
(*env)->DeleteLocalRef(env, hab);
return result;
}
return 0;
}
里面调用了initializeEncoding
J/* Initialize the fast encoding. If the "sun.jnu.encoding" property
* has not yet been set, we leave fastEncoding == NO_ENCODING_YET.
*/
static void
initializeEncoding(JNIEnv *env)
{
jstring propname = 0;
jstring enc = 0;

if ((*env)->EnsureLocalCapacity(env, 3) < 0)
return;

propname = (*env)->NewStringUTF(env, "sun.jnu.encoding");
if (propname) {
jboolean exc;
enc = JNU_CallStaticMethodByName
(env,
&exc,
"java/lang/System",
"getProperty",
"(Ljava/lang/String;)Ljava/lang/String;",
propname).l;
if (!exc) {
if (enc) {
const char* encname = (*env)->GetStringUTFChars(env, enc, 0);
if (encname) {
/*
* On Solaris with nl_langinfo() called in GetJavaProperties():
*
* locale undefined -> NULL -> hardcoded default
* "C" locale -> "" -> hardcoded default(on 2.6)
* "C" locale -> "ISO646-US"(on Sol 7/8)
* "en_US" locale -> "ISO8859-1"
* "en_GB" locale -> "ISO8859-1"(on Sol 7/8)
* "en_UK" locale -> "ISO8859-1"(on 2.6)
*/
if ((strcmp(encname, "8859_1") == 0) ||
(strcmp(encname, "ISO8859-1") == 0) ||
(strcmp(encname, "ISO8859_1") == 0))
fastEncoding = FAST_8859_1;
else if (strcmp(encname, "ISO646-US") == 0)
fastEncoding = FAST_646_US;
else if (strcmp(encname, "Cp1252") == 0 ||
/* This is a temporary fix until we move */
/* to wide character versions of all Windows */
/* calls. */
strcmp(encname, "utf-16le") == 0)
fastEncoding = FAST_CP1252;
else {
fastEncoding = NO_FAST_ENCODING;
jnuEncoding = (jstring)(*env)->NewGlobalRef(env, enc);
}
(*env)->ReleaseStringUTFChars(env, enc, encname);
}
}
} else {
(*env)->ExceptionClear(env);
}
} else {
(*env)->ExceptionClear(env);
}
(*env)->DeleteLocalRef(env, propname);
(*env)->DeleteLocalRef(env, enc);

/* Initialize method-id cache */
String_getBytes_ID = (*env)->GetMethodID(env, JNU_ClassString(env),
"getBytes", "(Ljava/lang/String;)[B");
String_init_ID = (*env)->GetMethodID(env, JNU_ClassString(env),
"<init>", "([BLjava/lang/String;)V");
}
static jboolean isJNUEncodingSupported = JNI_FALSE;
static jboolean jnuEncodingSupported(JNIEnv *env) {
jboolean exe;
if (isJNUEncodingSupported == JNI_TRUE) {
return JNI_TRUE;
}
isJNUEncodingSupported = (jboolean) JNU_CallStaticMethodByName (
env, &exe,
"java/nio/charset/Charset",
"isSupported",
"(Ljava/lang/String;)Z",
jnuEncoding).z;
return isJNUEncodingSupported;
}
上面的promt.c代码改为
#include <jni.h>
#include <stdio.h>
#include "Prompt.h"
#include "jni_util.h"

JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const char *str;
int len = (*env)->GetStringLength(env, prompt);
printf("%d\n",len);
str = (*env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL) {
return NULL; /* OutOfMemoryError already thrown */
}
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
/* We assume here that the user does not type more than
* 127 characters */
scanf("%s", buf);
return NewStringPlatform(env, buf);
}

运行Prompt.class,输入
邢红瑞
User typed: 邢红瑞
13
Type a line:
这就解决了中文平台java和jni的转码问题。

你可能感兴趣的:(jdk,C++,c,jni,C#)