Преобразование jbyteArray в массив символов и затем печать на консоль

Я пишу программу JNI, в которой мой файл .cpp получает jbyteArray, и я хочу иметь возможность распечатать jbyteArray с помощью printf. Я считаю, что для этого мне нужно преобразовать jbyteArray в массив символов.

Для базовых знаний Java-сторона моего JNI преобразует String в byteArray, а затем этот byteArray передается в качестве аргумента моей функции JNI.

То, что я сделал до сих пор, выводит строку правильно, но за ней следуют ненужные символы, и я не знаю, как избавиться от них / если я что-то делаю не так.

Вот что такое String:

dsa

и что выводит на консоль:

dsa,�

Нежелательные символы меняются в зависимости от того, что такое String. Вот часть кода, которая имеет отношение к делу:

.java файл:

public class tcr extends javax.swing.JFrame{

static{
    System.loadLibrary("tcr");
}

public native int print(byte file1[]);

    .....

    String filex1 = data1TextField.getText();//gets a filepath in the form of a String from a GUI jtextfield.
    byte file1[]= filex1.getBytes();//convert file path from string to byte array

        tcr t = new tcr();
        t.print(file1);
}

.cpp код:

JNIEXPORT jint JNICALL Java_tcr_print(JNIIEnv *env, jobject thisobj, jbyteArray file1){

    jboolean isCopy;
    jbyte* a = env->GetByteArrayElements(file1,&isCopy);
    char* b;
    b = (char*)a;
    printf("%s\n",b);
}

Любая помощь будет оценена по достоинству.


person Sean Sen Wang    schedule 05.07.2013    source источник


Ответы (2)


Посмотри, что ты делаешь:

jbyte* a = env->GetByteArrayElements(file1,&isCopy);

a теперь указывает на адрес памяти, где хранится байтовое содержимое строки. Предположим, что в файле есть строка «Hello world». В кодировке UTF-8 это будет:

48 65 6c 6c 6f 20 77 6f 72 6c 64

char* b = (char*)a;

b теперь указывает на эту область памяти. Это указатель на символ, поэтому вы, вероятно, захотите использовать его как строку C. Однако это не сработает. Строки C определяются как некоторые байты, заканчивающиеся нулевым байтом. Теперь посмотрите туда, и вы увидите, что в конце этой строки нет нулевого байта.

printf("%s\n",b);

Вот. Вы передаете указатель char на printf как %s, который сообщает printf, что это строка C. Однако это не строка C, но printf по-прежнему пытается напечатать все символы, пока не достигнет нулевого байта. Итак, то, что вы видите после dsa, на самом деле является байтами из вашей памяти после конца массива байтов, пока не будет (по совпадению) нулевой байт. Вы можете исправить это, скопировав байты в буфер, который на один байт длиннее, чем массив байтов, а затем установив последний элемент в ноль.

ОБНОВЛЕНИЕ:

Вы можете создать буфер большего размера и добавить нулевой байт следующим образом:

int textLength = strlen((const char*)a);
char* b = malloc(textLength + 1);
memcpy(b, a, textLength);
b[textLength] = '\0';

Теперь b является допустимой строкой C с завершающим нулем. Также не забудьте позвонить ReleaseByteArrayElements. Вы можете сделать это сразу после звонка memcpy.

person main--    schedule 05.07.2013
comment
Извините, если это звучит как вопрос для новичков, но как мне сделать буфер на один байт длиннее, а затем как мне отредактировать последний элемент? - person Sean Sen Wang; 08.07.2013
comment
спасибо, сработало как шарм. Небольшое редактирование, я использую C ++, а не C, поэтому мне нужно привести к (char *), когда выполняется вызов malloc. - person Sean Sen Wang; 09.07.2013

На самом деле jbyteArray - очень хороший способ передать строку Java через JNI. Он позволяет легко преобразовывать строку в набор символов и кодировку, необходимую для библиотек и файлов / устройств, которые вы используете на стороне C ++.

Убедитесь, что вы понимаете "Абсолютный минимум, что каждый разработчик программного обеспечения должен абсолютно точно знать о Юникоде и наборах символов (Нет Извинения!) "

Java String использует набор символов Unicode и кодировку UTF-16 (с зависящим от платформы порядком байтов).

String.getBytes () преобразуется в "кодировку платформы по умолчанию". Таким образом, он делает предположение о необходимом наборе символов и кодировке, а также о том, что делать с символами, которых нет в целевом наборе символов. Вы можете использовать другие перегрузки Java String.getBytes или методы Charset, если хотите явно управлять этими вещами.

При принятии решения, какой набор символов и кодировку использовать, учтите, что Unicode использовался в течение нескольких десятилетий в качестве основного строкового типа в Java, .NET, VB, ...; в исходных файлах компилятора для Java, ...; в основном в WWW. Конечно, вы можете быть ограничены тем, с чем хотите взаимодействовать.

Теперь кажется, что проблема, с которой вы столкнулись, заключается либо в том, что в целевом наборе символов отсутствуют символы, которые есть в вашей строке Java, и используется замена, либо используемая вами консоль не отображает их должным образом.

Консоль (или любое приложение с пользовательским интерфейсом), очевидно, должна выбрать гарнитуру для отображения символов. Гарнитуры обычно не поддерживают миллион кодовых точек, доступных в Unicode. Вы можете изменить конфигурацию своей консоли (или использовать другую). Например, в Windows можно использовать cmd.exe или ps (Windows PowerShell). Вы можете изменить шрифт в окнах Cmd.exe и использовать chcp для изменения набора символов.

ОБНОВИТЬ:

Как указывает @ main--, если вы используете функцию, которая ожидает, что терминатор добавлен к строке, вы должны предоставить его, обычно путем копирования массива, поскольку JVM сохраняет право собственности на массив. Это настоящая причина поведения в данном случае. Но все вышеперечисленное тоже актуально.

person Tom Blodget    schedule 05.07.2013
comment
В данном случае проблема не в этом. Посмотри на мой ответ. - person main--; 05.07.2013