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

来源:互联网 发布:频域滤波 python 编辑:程序博客网 时间:2024/05/01 15:13

严格的说,我基本没有做过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的转码问题。

 
原创粉丝点击