Как преобразовать jstring в wchar_t *

Предположим, что на стороне C++ моя функция принимает переменную типа jstring с именем myString. Я могу преобразовать его в строку ANSI следующим образом:

const char* ansiString = env->GetStringUTFChars(myString, 0);

есть ли способ получить

const wchar_t* unicodeString = ...

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
13
0
19 364
10

Ответы 10

Переносимым и надежным решением является использование iconv при том понимании, что вы должны знать, какую кодировку использует ваша система wchar_t (например, UTF-16 в Windows, UTF-32 во многих системах Unix).

Если вы хотите минимизировать свою зависимость от стороннего кода, вы также можете вручную настроить свой собственный конвертер UTF-8. Это легко, если преобразовать в UTF-32, несколько сложнее с UTF-16, потому что вам также нужно обрабатывать суррогатные пары. :-P Кроме того, вы должны быть осторожны, чтобы отклонить формы не самый короткий, иначе в некоторых случаях это может открыть ошибки безопасности.

Вы предлагаете преобразовать jstring в UTF-8, а затем обратно в UTF-16? Это действительно необходимо?

Rup 05.01.2012 17:01

Строки @Rup jstrings уже являются UTF-8: «JNI использует модифицированные строки UTF-8 для представления различных типов строк. Измененные строки UTF-8 такие же, как те, которые используются виртуальной машиной Java. Измененные строки UTF-8 кодируются таким образом, чтобы символ последовательности, содержащие только ненулевые символы ASCII, могут быть представлены с использованием только одного байта на символ, но могут быть представлены все символы Unicode ..... Java VM не распознает четырехбайтовый формат стандартного UTF-8; он использует вместо этого его собственный формат "два раза по три байта" ".

b1nary.atr0phy 23.05.2012 21:36

@ b1naryatr0phy Правда? jni.h в моей системе (как 1.6, так и 1.7) имеет typedef unsigned short jchar;, который для меня больше похож на UTF-16.

Rup 24.05.2012 04:08

Я, должно быть, что-то неправильно понимаю, эта цитата была взята непосредственно из документации Oracle: http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/type‌ s.html Не стесняйтесь объяснять, если можете, я все еще пытаюсь осмыслить это.

b1nary.atr0phy 24.05.2012 04:29

Если нас не интересует кроссплатформенность, в Windows вы можете использовать функцию MultiByteToWideChar или полезные макросы A2W (см. пример).

JNI также имеет функцию GetStringChars (). Тип возвращаемого значения - const jchar *, jchar - 16-битный на win32, поэтому он совместим с wchar_t. Не уверен, настоящий ли это UTF-16 или что-то еще ...

Вы случайно не знаете, совместим ли порядок байтов jchar с wchar_t Win32? Должно быть, но наверняка хорошо. :-)

Chris Jester-Young 16.09.2008 06:13

jchar определяется по типу unsigned short. Сам не пробовал, но думаю, да.

Adam Mitz 16.09.2008 06:19

char == jchar == беззнаковые 16 бит

b1nary.atr0phy 23.05.2012 21:10

char == 16 бит без знака на какой платформе?

Adam Mitz 24.05.2012 04:22

Чтобы это немного не сбивало с толку, в этом обсуждении «char» - это встроенный тип данных в языках ISO C и C++, а не в Java.

Adam Mitz 24.05.2012 06:59

Если jchar беззнаковый короткий, это означает, что он 16-битный. Таким образом, wchar_t также является коротким беззнаковым значением, поэтому 16 бит. В заключение jchar x = wchar_t y;)

Martin Krajčírovič 20.10.2018 19:07

Просто используйте env-> GetStringChars (myString, 0); Java передает Unicode по своей природе

Я знаю, что об этом спрашивали год назад, но мне не нравятся другие ответы, поэтому я все равно отвечу. Вот как мы это делаем в нашем источнике:

wchar_t * JavaToWSZ(JNIEnv* env, jstring string)
{
    if (string == NULL)
        return NULL;
    int len = env->GetStringLength(string);
    const jchar* raw = env->GetStringChars(string, NULL);
    if (raw == NULL)
        return NULL;

    wchar_t* wsz = new wchar_t[len+1];
    memcpy(wsz, raw, len*2);
    wsz[len] = 0;

    env->ReleaseStringChars(string, raw);

    return wsz;
}

РЕДАКТИРОВАТЬ: это решение хорошо работает на платформах, где wchar_t составляет 2 байта, некоторые платформы имеют 4 байта wchar_t, и в этом случае это решение не будет работать.

Это неправильное решение. Я сосал из-за этого 12 часов. wchar_t и jchar не обязательно одинаковы. Доказательством этого является результат моей тестовой программы: 01-26 20:28:43.675: E/[LMI-NATIVE](9280): len: 7, jchar: 2, wchar: 4.

Kobor42 26.01.2012 23:32

@ Kobor42 - Что делает ваша тестовая программа? Вы говорите, что нашли экземпляр, в котором wchar_t был 4 байта? На самом деле я этого не осознавал, но эта функция была разработана для запуска (в основном) в Windows, где wchar_t всегда равно 2. Теперь я понимаю, что wchar_t зависит от компилятора и может отличаться на вашей платформе.

Benj 27.01.2012 01:54

Точно. В Android до версии 2.1 wchar_t составляет 1 байт. 2.1 и после - 4 байта.

Kobor42 18.05.2012 13:16

Вы смешиваете потенциально несовместимые типы. Java jchar всегда имеет кодировку UTF-16. Но wchar_t не всегда UTF-16, иногда это UTF-32. В таких случаях вам необходимо преобразовать UTF-16 в UTF-32 (это НЕ просто вопрос заполнения jchar до 4 байтов, подробности см. В en.wikipedia.org/wiki/UTF-16).

rustyx 27.05.2012 00:27

Я не смешиваю. NDK смешивает это. Я хотел бы преобразовать строки java без потери информации в строки c.

Kobor42 12.07.2012 12:54

А кто освобождает wsz? Я бы порекомендовал STL!

std::wstring JavaToWSZ(JNIEnv* env, jstring string)
{
    std::wstring value;
    if (string == NULL) {
        return value; // empty string
    }
    const jchar* raw = env->GetStringChars(string, NULL);
    if (raw != NULL) {
        jsize len = env->GetStringLength(string);
        value.assign(raw, len);
        env->ReleaseStringChars(string, raw);
    }
    return value;
}

Не лучшее решение, если не использовать C++ 11, поскольку wstring будет возвращен по значению. (Очевидно, отправьте C++ 11, он будет построен, что будет эффективно)

Benj 10.01.2012 13:13

value.assign (raw, len); не действует. Я думаю, это должно быть value.assign (raw, raw + len); но я еще не тестировал.

mjaggard 17.04.2012 13:17

Отлично - отлично работал у меня в приложении C# -> C++ / CLI -> JNI -> Java!

bbqchickenrobot 30.08.2012 01:29

Разве вам не нужно вызывать ReleaseStringChars независимо от успеха GetStringChars, иначе jstring может быть закреплен и «просочиться»?

Greg Domjan 29.05.2015 04:51

Довольно просто. Но не забываем освобождать память с помощью ReleaseStringChars

JNIEXPORT jboolean JNICALL Java_TestClass_test(JNIEnv * env, jobject, jstring string)
{
    const wchar_t * utf16 = (wchar_t *)env->GetStringChars(string, NULL);
    ...
    env->ReleaseStringChars(string, utf16);
}

Если это кому-то поможет ... Я использовал эту функцию для проекта Android:

std::wstring Java_To_WStr(JNIEnv *env, jstring string)
{
    std::wstring value;

    const jchar *raw = env->GetStringChars(string, 0);
    jsize len = env->GetStringLength(string);
    const jchar *temp = raw;
    while (len > 0)
    {
        value += *(temp++);
        len--;
    }
    env->ReleaseStringChars(string, raw);

    return value;
}

Лучшее решение может быть (спасибо за отзыв):

std::wstring Java_To_WStr(JNIEnv *env, jstring string)
{
    std::wstring value;

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

    value.assign(raw, raw + len);

    env->ReleaseStringChars(string, raw);

    return value;
}

Аккуратно, хотя я подозреваю, что загрузка wstring с буфером за один раз будет более эффективной, чем по одному символу за раз.

Rup 02.02.2012 13:21

Компилятор C++ замечает, что вы возвращаете автоматическое значение, и размещаете его в куче, а не в стеке?

Stevens Miller 19.07.2016 23:50

Я пытаюсь jstring-> char-> wchar_t

char* js2c(JNIEnv* env, jstring jstr)
{
    char* rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("utf-8");
    jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0)
    {
        rtn = (char*)malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;
}

jstring c2js(JNIEnv* env, const char* str) {
    jstring rtn = 0;
    int slen = strlen(str);
    unsigned short * buffer = 0;
    if (slen == 0)
        rtn = (env)->NewStringUTF(str);
    else {
        int length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str, slen, NULL, 0);
        buffer = (unsigned short *)malloc(length * 2 + 1);
        if (MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length) > 0)
            rtn = (env)->NewString((jchar*)buffer, length);
        free(buffer);
    }
    return rtn;
}



jstring w2js(JNIEnv *env, wchar_t *src)
{
    size_t len = wcslen(src) + 1;
    size_t converted = 0;
    char *dest;
    dest = (char*)malloc(len * sizeof(char));
    wcstombs_s(&converted, dest, len, src, _TRUNCATE);

    jstring dst = c2js(env, dest);
    return dst;
}

wchar_t *js2w(JNIEnv *env, jstring src) {

    char *dest = js2c(env, src);
    size_t len = strlen(dest) + 1;
    size_t converted = 0;
    wchar_t *dst;
    dst = (wchar_t*)malloc(len * sizeof(wchar_t));
    mbstowcs_s(&converted, dst, len, dest, _TRUNCATE);
    return dst;
}

Вот как я преобразовал jstring в LPWSTR.

const char* nativeString = env->GetStringUTFChars(javaString, 0);
size_t size = strlen(nativeString) + 1;
LPWSTR lpwstr = new wchar_t[size];
size_t outSize;
mbstowcs_s(&outSize, lpwstr, size, nativeString, size - 1);

Другие вопросы по теме