IT_Programming/Java

[펌] Java Native Interface (JNI) - Strings in JNI

JJun ™ 2009. 4. 13. 00:32

 

출처: http://www.manghon.com/blog/400 

 

Strings in JNI


  자바에서의 String 은 JNI 코드에서 jstring 타입으로 사용할 수 있다. 이 jstring 타입은 C, C++ 코드에서 바로 사용할 수는 없다. C언어의 출력 함수인 printf 함수에서 jstring 타입을 출력하려 할때 JVM에서 충돌이 일어날 수 있다. jstring 을 사용하기 위해서는 먼저 JNI 에서 제공하는 string 변환 함수들을 이용하여 C-string 타입으로 변환해야 한다.

  Java 프로그램에서 native 환경으로 string 이 전달될때 UTF-8 형식으로 변경된다.
  다음 그림은 UTF-8 인코딩이 메모리에서 구조화되는 모습을 보여준다.

 

  만약 바이트의 high bit 가 1이라면 그 바이트는 멀티바이트 캐릭터의 부분이다. 이것이 의미하는 것은

  ASCII 에서 사용하는 1~127까지의 값은 그대로 사용된다는 의미이고, jstring 의 모든 character 가

  이 범위에 속한다면 변환없이 C/C++ 코드에서 바로 사용할 수 있음을 의미한다. 자바에서는 3바이트 보다

  큰 멀티바이트 UTF-8 인코딩은 사용하지 않으며, NULL character ( ASCII 0 )은 2바이트로 표현한다.

  이 의것의 의미는 모든 비트가 0인 케릭터를 가질수 없음을 의미한다.



  스트링을 다루는 루틴이 하나 더 있는데, 2바이트로 인코딩된 유니코드를 사용하는 것이다. 만약 프로그램에서 지역화된 스트링(??)을 사용했을 경우, 항상 유니코드 스트링으로 처리해야 한다. 왜냐하면 UTF-8은 국제화를 지원하지 않기 때문이다.

  UTF-8 을 다루는 5가지 함수가 있는데 이 함수들은 2바이트 유니코드 인코딩을 카운트할 수 있는 부분을

가진다. 또 스트링을 해제(아마 음... free 또는 delete 개념인듯) 하는 2개의 함수가 있다. 그리고 자바에서

사용되는 synchronized 기능을 위해 스트링의 lock 과 release 를 담당하는 메소드 2개가 있다.

  각 함수들은 첫번째 인자로 Java environment 의 pointer 를 받는다. 이것은 자바에서 native 메소드를

호출할 때 자동으로 전달하는 인자이므로 쉽게 사용할 수 있다.

jstring NewString(const jchar *unicodeChars, jsize len);
jstring NewStringUTF(const char *bytes);

  위 첫번째 NewString 함수는 jchar 의 시퀀스 (즉, 2바이트를 사용하는 jchar 타입 캐릭터의 연속)를 받고, 캐릭터의 길이를 인자로 받는다. 두번째 UTF 버전은 단순히 char (즉, byte) 의 연속을 받는다. 각 바이트들은 1, 2, 3 바이트인 멀티바이트 캐릭터일 것이다. 그리고 스트링의 끝은 2바이트 NULL 캐릭터로 나타낸다.

jsize GetStringLength(jstring string);
jsize GetStringUTFLength(jstring string);

  위 두 메소드는 jstring을 인자로 받아서 유니코드와 UTF 형식일때의 캐릭터 숫자를 반환한다.

const jchar *GetStringChars(jstring string, jboolean *isCopy);
const char *GetStringUTFChars(jstring string, jboolean *isCopy);

  위 두 메소드는 주어진 jstring 의 포인터를 반환한다. 이 두 메소드는 jstring 을 native 환경에서 사용하기

쉬운 string 으로 변환하기 위해 자주 사용하는 함수들이다. 이 함수가 반환한 포인터는 대응되는 버전의 ReleaseStringChars 함수가 호출되기 전까지 유효하다. 첫번째 버전은 jchar 타입의 포인터를, 두번째 버전인 UTF 버전은 jbyte(1바이트..함수의 리턴타입은 char *이다) 타입의 포인터를 리턴한다. isCopy 인자에 JNI_TRUE 를 주면 string의 복사본을 만들고 그 포인터를 리턴하고, NULL 이나 JNI_FALSE 를 주면,

복사하지 않고 원본의 포인터를 리턴한다.

  다음 ReleaseStringChars 함수들은 VM 에게 native 환경에서 더 이상 스트링을 사용하지 않는다고 말한다.(free 개념인듯 가비지 콜렉팅 대상으로..ㅋ)

void ReleaseStringChars(jstring string, const jchar *chars);
void ReleaseStringUTFChars(jstring string, const char *utf);

  위 함수가 호출되고 나면 더 이상 jstring 으로 들어간 인자가 가리키는 스트링은 유효하지 않게 된다.

  다음 두 함수는 jstring 인자에서 start에서 시작하여 len-1 까지의 substring 을 buf 에 넣어주는 함수들이다.

void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf);
void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf);

  위 두 함수는 StringIndexOutOfBoundsException 을 발생시킬 수 있다.

  다음 GetStringCritical 함수는 주어진 jstring 타입의 jchar * 타입 포인터를 반환한다.

이 함수는 다중 스레드 환경에서 사용되는 스트링의 잠금 역할을 한다.

const jchar *GetStringCritical(jstring string, jboolean *isCopy);

  필요하다면, isCopy 인자로 스트링을 복사할 수도 있다.

이 스트링의 사용이 끝나면 ReleaseStringCritical 함수를 호출하여 잠금을 해제할 수 있다.

void ReleaseStringCritical(jstring string, const jchar *carray);

ReleaseStringCritical 함수는 GetStringCritical 함수를 호출하여 얻은 포인터를 릴리즈 한다.

 

===========================  스트링을 사용하는 예제 프로그램을 첨부한다. ===========================

JNIStringFunctions.java

public class JNIStringFunctions
{
    public native String replaceString(String srcStr, String strToReplce, String replaceStr);
   
    static
    {
        System.loadLibrary("JNIStringFunctions");
    }
   
    public static void main(String[] args)
    {
        JNIStringFunctions test = new JNIStringFunctions();
        String one = "manghon";
       
        String temp = test.replaceString(one, "n", "Abc");
        System.out.println( temp );
    }
}

JNIStringFunctions.cpp 중에서 일부분...

JNIEXPORT jstring JNICALL Java_JNIStringFunctions_replaceString(JNIEnv *env, jobject obj, jstring _srcString, jstring _strToReplace, jstring _replString)
{
    const char *searchStr, *findStr, *replStr, *found;
    jstring newString = NULL;
    int index;

    searchStr = env->GetStringChars(_srcString, NULL);
    findStr = env->GetStringChars(_strToReplace, NULL);
    replStr = env->GetStringChars(_replString, NULL);

    found = strstr(searchStr, findStr);

    if( found != NULL )
    {
        char *newStringTemp;

        index = found - searchStr;
        newStringTemp = new char[strlen(searchStr) + strlen(replStr) + 1];

        strcpy(newStringTemp, searchStr);
        newStringTemp[index] = 0;

        strcat(newStringTemp, replStr);
        strcat(newStringTemp, &searchStr[index+strlen(findStr)]);

        newString = env->NewStringUTF( (const char*)newStringTemp);
        delete[] newStringTemp;
    }

    env->ReleaseStringUTFChars(_srcString, searchStr);
    env->ReleaseStringUTFChars(_strToReplace, findStr);
    env->ReleaseStringUTFChars(_replString, replStr);

    return (newString);
}

아래는 완전한 이클립스 프로젝트와 비주얼 스튜디오 프로젝트를 각각 압축하여 놓았다.

 

JNIStringFunctions.zip

JNIStudy.zip

JNIStringFunctions.zip
2.83MB
JNIStudy.zip
0.01MB