Ответ 1
Это ясно объясняется в документации Java:
GetStringUTFChars
const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);
Возвращает указатель на массив байтов, представляющий строку в модифицированной кодировке UTF-8. Этот массив действителен до тех пор, пока он не будет выпущен ReleaseStringUTFChars().
JNI использует измененные строки UTF-8 для представления различных типов строк. Модифицированные строки UTF-8 такие же, как те, что используются виртуальной машиной Java. Измененные строки UTF-8 кодируются так, что последовательности символов, которые содержат только ненулевые символы ASCII, могут быть представлены с использованием только одного байта на символ, но могут быть представлены все символы Юникода.
Все символы в диапазоне от
\u0001
до\u007F
представлены одним байтом, как показано ниже:Семь бит данных в байте дают значение представленного символа.
Нулевой символ (
'\u0000'
) и символы в диапазоне от'\u0080'
до'\u07FF'
представлены парой байтов x и y:Байты представляют символ со значением
((x & 0x1f) << 6) + (y & 0x3f)
.Символы в диапазоне от
'\u0800'
до'\uFFFF'
представлены тремя байтами x, y и z:Символ со значением
((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f)
представлен байтами.Символы с кодовыми точками выше U + FFFF (так называемые дополнительные символы) представлены отдельно кодированием двух суррогатных единиц кода их представления UTF-16. Каждый из суррогатных единиц кода представлен тремя байтами. Это означает, что дополнительные символы представлены шестью байтами, u, v, w, x, y и z:
Символ со значением
0x10000+((v&0x0f)<<16)+((w&0x3f)<<10)+(y&0x0f)<<6)+(z&0x3f)
представлен шестью байтами.Байты многобайтовых символов хранятся в файле класса в порядке big-endian (старший байт).
Существуют два различия между этим форматом и стандартным форматом UTF-8. Во-первых, нулевой символ (char) 0 кодируется с использованием двухбайтового формата, а не однобайтного формата. Это означает, что измененные строки UTF-8 никогда не имеют встроенных нулей. Во-вторых, используются только однобайтовые, двухбайтовые и трехбайтовые форматы стандартного UTF-8. Java VM не распознает четырехбайтовый формат стандартного UTF-8; вместо этого он использует свой собственный двух-трехбайтовый формат.
Дополнительные сведения о стандартном формате UTF-8 см. в разделе 3.9 Форматы кодировки Unicode стандарта Unicode версии 4.0.
Так как U + 1F604 является дополнительным символом, а Java не поддерживает формат 4-байтового кодирования UTF-8, U + 1F604 представлен в модифицированном UTF-8 путем кодирования суррогатной пары UTF-16 U+D83D U+DE04
с использованием 3 байтов на суррогат, таким образом, 6 байтов.
Итак, чтобы ответить на ваш вопрос...
Есть ли простой способ преобразования строки Java в настоящий байтовый массив UTF-8 в коде JNI?
Вы можете:
-
Используйте
GetStringChars()
, чтобы получить исходные кодированные символы UTF-16, а затем создайте свой собственный массив байтов UTF-8. Преобразование из UTF-16 в UTF-8 - очень простой алгоритм для реализации вручную. -
Попросите код JNI вернуться в Java, чтобы вызвать метод
String.getBytes(String charsetName)
для кодирования объектаjstring
в UTF -8 байтовый массив, например:JNIEXPORT void JNICALL Java_EmojiTest_nativeTest(JNIEnv *env, jclass cls, jstring _s) { const jclass stringClass = env->GetObjectClass(_s); const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B"); const jstring charsetName = env->NewStringUTF("UTF-8"); const jbyteArray stringJbytes = (jbyteArray) env->CallObjectMethod(_s, getBytes, charsetName); env->DeleteLocalRef(charsetName); const jsize length = env->GetArrayLength(stringJbytes); const jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL); for (int i = 0; i < length; ++i) fprintf(stderr, "%d: %02x\n", i, pBytes[i]); env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT); env->DeleteLocalRef(stringJbytes); }
Статья в Википедии UTF-8 предполагает, что GetStringUTFChars() фактически возвращает CESU-8, а не UTF-8
Java Modified UTF-8 не совсем то же самое, что CESU-8:
CESU-8 похож на Java Modified UTF-8, но не имеет специального кодирования символа NUL (U + 0000).