Esiste un modo semplice per convertire una stringa Java in un vero array di byte UTF-8 nel codice JNI?Ottenimento di caratteri UTF-8 in Java JNI
Sfortunatamente GetStringUTFChars() quasi fa ciò che è richiesto ma non del tutto, restituisce una sequenza di byte UTF-8 "modificata". La differenza principale è che un UTF-8 modificato non contiene alcun carattere null (quindi è possibile trattare una stringa terminata da null ANSI C) ma un'altra differenza sembra essere il modo in cui vengono trattati i caratteri supplementari Unicode come le emoji.
Un carattere come U + 1F604 "FACCIA SORRIDENTE CON BOCCA APERTA E OCCHI SORRIDENTI" è memorizzato come una coppia di surrogati (due caratteri UTF-16 U + D83D U + DE04) e ha un equivalente UTF-8 a 4 byte di F0 9F 98 84, e cioè la sequenza di byte che ricevo se convertire la stringa UTF-8 in Java:
char[] c = Character.toChars(0x1F604);
String s = new String(c);
System.out.println(s);
for (int i=0; i<c.length; ++i)
System.out.println("c["+i+"] = 0x"+Integer.toHexString(c[i]));
byte[] b = s.getBytes("UTF-8");
for (int i=0; i<b.length; ++i)
System.out.println("b["+i+"] = 0x"+Integer.toHexString(b[i] & 0xFF));
il codice sopra stampa il seguente:
c [ 0] = 0xd83d c [1] = 0xde04 b [0] = 0xf0 b [1] = 0x9F b [2] = 0x98 b [3] = 0x84
Tuttavia, 's' se mi passa in un metodo JNI nativo e chiamo GetStringUTFChars() ottengo 6 byte. Ciascuno dei caratteri surrogati al doppino viene convertito a una sequenza di 3 byte indipendentemente:
JNIEXPORT void JNICALL Java_EmojiTest_nativeTest(JNIEnv *env, jclass cls, jstring _s)
{
const char* sBytes = env->GetStringUTFChars(_s, NULL);
for (int i=0; sBytes[i]!=0; ++i)
fprintf(stderr, "%d: %02x\n", i, sBytes[i]);
env->ReleaseStringUTFChars(_s, sBytes);
return result;
}
0: ED 1: a0 2: bd 3: Ed 4: b8 5: 84
Il Wikipedia UTF-8 article suggerisce che GetStringUTFChars() restituisce effettivamente CESU-8 anziché UTF-8. Che a sua volta fa sì che il mio codice nativo per Mac in crash, perché non è una valida sequenza UTF-8:
CFStringRef str = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
CFURLRef url = CFURLCreateWithFileSystemPath(NULL, str, kCFURLPOSIXPathStyle, false);
Suppongo che potrei cambiare tutti i miei metodi JNI per prendere un byte [], piuttosto che una stringa e fare l'UTF -8 conversione in Java ma che sembra un po 'brutta, c'è una soluzione migliore?