на главную обучение сертификация статьи литература ссылки гостевая книга
  Список статей Оглавление Текст статьи  

Java: Русские буквы и не только...

Автор:Астахов Сергей
Создан:03.06.2003


15. JNI

   JNI (Java Native Interface) - это стандарт по взаимодействию с C/C++-ным кодом. Как и следовало ожидать, на этом водоразделе тоже происходит столкновение байтов и символов. Большинство C/C++-ных программ пишется без учёта Unicode, многие программисты даже не знают о нём. Я сам, за 7 лет писательства на C/C++, пока не начал писать на Java, про Unicode знал только по наслышке. Большинство строковых операций в C/C++ сделаны для 8-битового сишного типа char. В принципе, есть некоторые подвижки в этом направлении, в частности для Windows NT можно откомпилировать код, который будет взаимодействовать с Unicode-вариантами Win32 API, но, к сожалению, этого часто недостаточно.

   Таким образом главная задача - получить тип char* из типа jstring (JNI-шное отображение String) и наоборот. Практически во всех описаниях и примерах JNI для этого используется пара функций GetStringUTFChars()/ReleaseStringUTFChars(). Коварные буржуины и здесь приготовили засаду - эти функции формируют массив байтов по стандарту UTF, который соответствует ожидаемому только для ASCII-символов (первых 128 значений). Русские буквы опять в пролёте. Сишные строки char* очень хорошо ложатся на Java-овский тип byte[], но при этом возникает загвоздка в виде ноль-символа. Его нужно добавлять при преобразовании byte[]->char* и учитывать при обратном преобразовании.
Пример:
  private native int nAction(String msg);
  private native String nGetErrorString(int error);
  
  public void action(String msg)throws java.io.IOException
  {
    int res=nAction(msg);
    if(res!=0) throw new java.io.IOException(nGetErrorString(res));
  }

  ...

  jbyteArray getStringBytes(JNIEnv *env, jstring str)
  {
    if(!str) return NULL;
    jmethodID getBytes=
	  env->GetMethodID(env->GetObjectClass(str),"getBytes","()[B");
    jbyteArray buf=(jbyteArray)env->CallObjectMethod(str,getBytes);
    if(!buf) return NULL;

    // Добавляем ноль-символ
    jsize len=env->GetArrayLength(buf);
    jbyteArray nbuf = env->NewByteArray(len+1);
    if(len!=0)
    {
      jbyte *cbuf=env->GetByteArrayElements(buf,NULL);
      env->SetByteArrayRegion(nbuf,0,len,cbuf);
      env->ReleaseByteArrayElements(buf,cbuf,JNI_ABORT);
    }
    env->DeleteLocalRef(buf);
    return nbuf;
  }


  JNIEXPORT jint JNICALL Java_Test_nAction
    (JNIEnv *env, jobject obj, jstring msg)
  {
    jbyteArray bmsg=getStringBytes(env,msg);
    if(!bmsg) return -1;
    jbyte *cmsg=env->GetByteArrayElements(bmsg,NULL);
    printf(cmsg);
    jint res=do_something(cmsg);
    env->ReleaseByteArrayElements(bmsg,cmsg,JNI_ABORT);
    return res;
  }

  jstring newString(JNIEnv *env, jbyteArray jbuf, int len)
  {
    jclass stringClass=env->FindClass("java/lang/String");
    if(!stringClass) return NULL;
    jmethodID init=env->GetMethodID(stringClass,"","([BII)V");
    if(!init) return NULL;
    return (jstring)env->NewObject(stringClass,init,jbuf,0,len);
  }

  jstring newString(JNIEnv *env, const char *buf)
  {
    if(!buf) return NULL;
    int bufLen=strlen(buf);
    if(bufLen==0)
    {
      return env->NewString((const jchar *)L"",0);
    }
    jbyteArray jbuf=env->NewByteArray(bufLen);
    if(!jbuf) return NULL;
    env->SetByteArrayRegion(jbuf,0,bufLen,(jbyte*)buf);
    jstring jstr=newString(env,jbuf,bufLen);
    env->DeleteLocalRef(jbuf);
    return jstr;
  }

  JNIEXPORT jstring JNICALL Java_Test_nGetErrorString
    (JNIEnv *env, jobject obj, jint error)
  {
    char cmsg[256];
    memset(cmsg,0,sizeof(cmsg));
    get_error_string(error,cmsg,sizeof(cmsg));
    return newString(env,cmsg);
  }
   Тут используется преобразование символов по умолчанию, что вполне естественно при взаимодействиях с системным API. Если же Вам необходима определённая кодовая страница, соответственно нужно добавить её название.


назад оглавление дальше