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.
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.
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.