将jbyteArray转换为字符数组,然后打印到控制台

时间:2013-07-05 14:56:14

标签: java-native-interface type-conversion

我正在编写一个JNI程序,其中.cpp文件获取jbyteArray,我希望能够使用printf打印jbyteArray。为此,我相信我必须将jbyteArray转换为字符数组。

对于背景知识,我的JNI的java端将String转换为byteArray,然后将byteArray作为参数传递给我的JNI函数。

到目前为止,我已经完成了正确的打印字符串,但接下来是垃圾字符,我不知道如何摆脱这些/如果我做错了。

以下是字符串:

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);
}

任何帮助都将不胜感激。

2 个答案:

答案 0 :(得分:7)

看看你在做什么:

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现在指向该内存区域。它是一个char指针,因此您可能希望将其用作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是一个有效的以null结尾的C字符串。另外,不要忘记拨打ReleaseByteArrayElements。您可以在memcpy电话后立即执行此操作。

答案 1 :(得分:2)

jbyteArray实际上是通过JNI传递Java String的一种非常好的方法。它允许您轻松地将字符串转换为您在C ++端使用的库和文件/设备所需的字符集和编码。

请务必理解“The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

Java String使用Unicode字符集和UTF-16编码(具有与平台相关的字节顺序)。

String.getBytes()转换为“平台的默认字符集”。因此,它假设您需要的字符集和编码,以及如何处理不在目标字符集中的字符。如果要显式控制这些内容,可以使用其他Java String.getBytes重载或Charset方法。

在决定使用哪个字符集和编码时,请考虑Unicode已经使用了几十年作为Java,.NET,VB中的主要字符串类型......;在Java的编译器源文件中,...;一般在WWW。当然,您可能会受到要与之互操作的东西的限制。

现在,您面临的问题似乎是目标字符集缺少Java String所具有的字符并且正在使用替代字符,或者您正在使用的控制台未正确显示它们。

显然,控制台(或任何带有UI的应用程序)必须选择用于呈现字符的字体。字体通常不支持Unicode中可用的百万个代码点。您可以更改控制台的配置(或使用其他配置)。例如,在Windows中,您可以使用cmd.exe或ps(Windows PowerShell)。您可以在Cmd.exe窗口中更改字体,并使用chcp更改字符集。

更新:

正如@ main--所指出的,如果你使用一个需要在字符串后附加一个终结符的函数,那么你必须提供它,通常是通过复制数组来提供它,因为JVM保留了数组的所有权。这是这种情况下行为的实际原因。但是,所有这些都是相关的。