Question

In my Android JNI code, I need to convert jstring to wchar_t. The closest reference I found was How do I convert jstring to wchar_t *.

One can obtain jchar* and the length using the following code:

const jchar *raw = env->GetStringChars(string, 0);
jsize len = env->GetStringLength(string);

wchar_t* wStr = new wchar_t[len+1];

It seems I cannot use wcncpy to copy "raw" into "wStr." Although jchar is 2-bytes long, wchar_t is 4 bytes long on all modern versions of Android OS.

One option is to copy one character at a time in a for loop:

for(int i=0;i<len;i++) {
    wStr[i] = raw[i];
}
wStr[len] = 0;

The other option would be to call env->GetStringUTFChars() and use iconv_* routines to convert to wchar_t type.

Can someone please confirm if option 1 is valid? Hope I don't have to resort to option 2. Is there a better option? Regards.

Was it helpful?

Solution 3

As long as all your data is UCS2, you can use option 1. Please see wchar_t for UTF-16 on Linux? for a similar discussion. Note that C++11 provides std::codecvt_utf16 to deal with the situation.

OTHER TIPS

wchar_t specifies an element size but not a character set or encoding. Since you are asking about a 32-bit element, can we assume you want to use Unicode/UTF-32? Regardless, once you decide which encoding you want, standard Java libraries are up to the task.

Use a String.getBytes() overload to get an array of bytes. (It is easier to do this in Java rather than JNI, if you have a choice.) Once you have a jbyteArray, you can copy it to a C buffer and cast to wchar_t *.

On Android, you might want Unicode/UTF-8. But that has an 8-bit code-unit so you probably wouldn't be asking about wchar_t. (BTW-a character in UTF-8 can need 1 or more bytes.)

One way would be to use String.getBytes("UTF-32LE"). Note this is making the ASSUMPTION that wchar_t is 4 bytes and little-endian, but this should be a fairly safe assumption to make.

Here's an example that passes a String from Java to C++, where it is converted to std::wstring, reversed, and passed back to Java:

class MyClass {
    private native byte[] reverseString(byte[] arr);
    String reverseString(String s) {
        try {
            return new String(reverseString(s.getBytes("UTF-32")), "UTF-32");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return "";
        }
    }
}

On the C++ side you have:

std::wstring toWStr(JNIEnv *env, jbyteArray s)
{
    const wchar_t *buf = (wchar_t*) env->GetByteArrayElements(s, NULL);
    int n = env->GetArrayLength(s) / sizeof(wchar_t);

    // First byte is BOM (0xfeff), so we skip it, hence the "buf + 1".
    // There IS NO null-terminator.
    std::wstring ret(buf + 1, buf + n);

    env->ReleaseByteArrayElements(s, (jbyte*) buf, 0);
    return ret;
}

jbyteArray fromWStr(JNIEnv *env, const std::wstring &s)
{
    jbyteArray ret = env->NewByteArray((s.size()+1)*sizeof(wchar_t));

    // Add the BOM in front.
    wchar_t bom = 0xfeff;
    env->SetByteArrayRegion(ret, 0, sizeof(wchar_t), (const jbyte*) &bom);

    env->SetByteArrayRegion(ret, sizeof(wchar_t), s.size()*sizeof(wchar_t), (const jbyte*) s.c_str());
    return ret;
}

extern "C" JNIEXPORT jbyteArray JNICALL Java_MyClass_reverseString(JNIEnv *env, jobject thiz, jbyteArray arr)
{
    std::wstring s= toWStr(env, arr);
    std::reverse(s.begin(), s.end());
    return fromWStr(env, s);
}

I tested it both on my phone, which has Android 4.1.2 and ARM CPU, and on the Android Emulator - Android 4.4.2 and x86 CPU, and this code:

MyClass obj = new MyClass();
Log.d("test", obj.reverseString("hello, здравствуйте, 您好, こんにちは"));

Gave this output:

06-04 17:18:20.605: D/test(8285): はちにんこ ,好您 ,етйувтсвардз ,olleh

No need to convert. Cast const jchar to (wchar_t *). jni.h define jchar as typedef uint16_t jchar; /* unsigned 16 bits */ which is eventually wchar_t.

You can try this, it worked for me in old project.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top