Android JNI - 将jstring转换为wchar_t的可靠方法

时间:2014-04-16 21:07:31

标签: android android-ndk java-native-interface

在我的Android JNI代码中,我需要将jstring转换为wchar_t。我发现的最近参考是How do I convert jstring to wchar_t *

可以使用以下代码获取jchar *和长度:

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

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

似乎我不能使用wcncpy复制" raw"进入" wStr。"虽然jchar的长度为2个字节,但在所有现代版本的Android OS上,wchar_t的长度为4个字节。

一种选择是在for循环中一次复制一个字符:

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

另一种选择是调用env-&gt; GetStringUTFChars()并使用iconv_ *例程转换为wchar_t类型。

有人可以确认选项1是否有效?希望我不必诉诸于选项2.有更好的选择吗?问候。

4 个答案:

答案 0 :(得分:1)

wchar_t指定元素大小,但不指定字符集或编码。既然你问的是32位元素,我们可以假设你想使用Unicode / UTF-32吗?无论如何,一旦确定了所需的编码,标准Java库就可以完成任务。

使用String.getBytes()重载来获取字节数组。 (如果你有选择的话,在Java而不是JNI中更容易做到这一点。)一旦有了jbyteArray,就可以将它复制到C缓冲区并转换为wchar_t *

在Android上,您可能需要Unicode / UTF-8。但是它有一个8位代码单元,所以你可能不会问wchar_t。 (BTW- UTF-8中的字符可能需要1个或更多字节。)

答案 1 :(得分:1)

一种方法是使用String.getBytes("UTF-32LE")。注意这是假设wchar_t是4个字节和小端的假设,但这应该是一个相当安全的假设。

这是一个将String从Java传递到C ++的示例,它将转换为std :: wstring,反转并传递回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 "";
        }
    }
}

在C ++方面你有:

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);
}

我在手机上测试了它,它有Android 4.1.2和ARM CPU,在Android模拟器上测试 - Android 4.4.2和x86 CPU,以及此代码:

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

给出了这个输出:

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

答案 2 :(得分:0)

只要您的所有数据都是UCS2,就可以使用选项1 。有关类似的讨论,请参阅 wchar_t for UTF-16 on Linux? 。请注意,C ++ 11提供了std::codecvt_utf16来处理这种情况。

答案 3 :(得分:-1)

无需转换。将const jchar投射到(wchar_t *)。 jni.h将jchar定义为typedef uint16_t jchar; /* unsigned 16 bits */,最终为wchar_t

你可以试试这个,它在旧项目中对我有用。