Erste echte UTF-8-Zeichen in Java-JNI
Gibt es eine einfache Möglichkeit zur Konvertierung einer Java-string zu einem echten UTF-8-byte-array im JNI code?
Leider GetStringUTFChars() fast tut, was erforderlich ist, aber nicht ganz, es gibt eine "modifizierte" UTF-8-byte-Sequenz. Der Hauptunterschied ist, dass eine modifizierte UTF-8 enthält keine null-Zeichen (so dass Sie behandeln können, ist eine ANSI-C-null-terminated string), sondern ein anderer Unterschied scheint zu sein, wie Unicode-Sonderzeichen wie emoji behandelt werden.
Ein Zeichen wie U+1F604 "LÄCHELNDES GESICHT MIT OFFENEM MUND UND LÄCHELNDEN AUGEN" gespeichert ist, als ein Surrogat-paar (zwei UTF-16-Zeichen U+D83D U+DE04) und hat einen 4-byte-UTF-8 äquivalent F0 9F 98 84, und das ist die byte-Sequenz, die ich bekomme, wenn ich konvertieren Sie die Zeichenfolge in 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));
Der obige code druckt die folgenden:
?
c[0] = 0xd83d
c[1] = 0xde04
b[0] = 0xf0
b[1] = 0x9f
b[2] = 0x98
b[3] = 0x84
Allerdings, wenn ich pass 's' in eine native JNI-Methode, und rufen GetStringUTFChars() bekomme ich 6 bytes. Jeder der die ersatzzeichenpaare wird umgebaut zu einem 3-byte-Sequenz unabhängig:
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
Den Wikipedia UTF-8 Artikel deutet darauf hin, dass GetStringUTFChars() eigentlich zurück CESU-8 anstatt UTF-8. Das wiederum bewirkt, dass meine native Mac-code zum Absturz bringen, weil es keine gültige UTF-8-Sequenz:
CFStringRef str = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
CFURLRef url = CFURLCreateWithFileSystemPath(NULL, str, kCFURLPOSIXPathStyle, false);
Ich nehme an, ich könnte alle meine JNI-Methoden nehmen ein byte[] statt String und tun die UTF-8 Konvertierung in Java, aber das scheint ein bisschen hässlich, gibt es eine bessere Lösung?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dies ist klar und deutlich erklärt, in der Java-Dokumentation:
JNI-Funktionen
Modifiziertes UTF-8
Da U+1F604 ist eine Ergänzende Charakter-und Java unterstützt keine UTF-8-4-byte-encoding-format, U+1F604 ist vertreten im modifizierten UTF-8 als Kodierung UTF-16-ersatzpaar
U+D83D U+DE04
mit 3 Byte pro Surrogat, also 6 bytes insgesamt.Also, um deine Frage zu beantworten...
Können Sie entweder:
Verwenden
GetStringChars()
um die ursprüngliche UTF-16-codierte Zeichen, und dann erstellen Sie Ihre eigenen UTF-8-byte-array ab. Die Konvertierung von UTF-16 zu UTF-8 ist ein sehr einfach-Algorithmus zu implementieren, indem Sie von hand ein, oder verwenden Sie eine bereits bestehende implementation von Ihrer Plattform oder 3rd-party-Bibliotheken.Haben Ihre JNI-code-Aufruf wieder in Java aufrufen, die
- String.getBytes(String charsetName)
Methode zur Kodierung derjstring
Objekt in eine UTF-8-byte-array, z.B.:Java ' s Modified UTF-8 ist nicht genau das gleiche wie CESU-8:
char*
Punkte zu wahren UTF-8-Daten und nicht "geändert" UTF-8 Daten, 1) manuell Dekodieren von UTF-8 nach UTF-16 und dann übergeben, die JNINewString()
Funktion, oder 2) mit JNI zu kopierenchar
Daten auf einem Java -byte[]
array und übergeben, dieString
Konstruktor, einebyte[]
- und charset-name als Eingabe die Angabe "UTF-8" als Zeichensatz.char*
Punkte auf "modifiziert" UTF-8-Daten, dann können Sie einfach die JNINewStringUTF()
Funktion durch sich selbst.