如何在JNI中将Java字节数组的内容转换为C字符串? [英] How to convert the contents of a Java byte array to C string in JNI?

查看:93
本文介绍了如何在JNI中将Java字节数组的内容转换为C字符串?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:

错误:jbyte* elements = (*env)->GetByteArrayElements(env, array, NULL);仅返回8个字节.提供检索字节形式jbytearray的方法的任何替代方法.

Error: jbyte* elements = (*env)->GetByteArrayElements(env, array, NULL); returns only 8 bytes. Provide any alternative to way to retrieve byte form jbytearray.

我是JNI的新手,所以我对JNI和英语都不熟悉.

I'm new in JNI so I'm not familiar in JNI and also English.

现在,我尝试使用Java读取文件的简单JNI程序,然后使用C将其写入文件.

Now I try the simple JNI program on File reading in Java and write it into file using C.

文件读取Java代码:

File reading java code:

public class FileIO {
   static {
      System.loadLibrary("io");         
   }

   private native void writeToFile(byte[] msg);

   public static void main(String[] args) throws IOException { 
      FileInputStream fileInputStream=null;

      File file = new File("version1-1.png");

      byte[] bFile = new byte[(int) file.length()];

      try {
         //convert file into array of bytes
         fileInputStream = new FileInputStream(file);
         fileInputStream.read(bFile);
         fileInputStream.close();
         System.out.println("Done");

      } catch(Exception e){
         e.printStackTrace();
      }

      new FileIO().writeToFile(bFile); 
   }
}

文件编写C代码:

#include <jni.h>
#include <stdio.h>
#include <string.h>
#include "FileIO.h"

JNIEXPORT void JNICALL Java_FileIO_writeToFile (JNIEnv *env, jobject job, jbyteArray array ){

    char * buf ; 

   // here what i do ???    :(

    FILE *file = fopen("test123.png", "w");

    int results = fputs(buf, file);
    if (results == EOF) {
        //Failed to write do error code here.
    }
    fclose(file);
}

我尝试了许多解决方案(在链接下方),但是将其写入文件并不算运气.请提供正确的解决方案和最佳的JNI教程站点.

I have tried many solutions (below link) but no luck in writing it to the file. Please provide the correct solution and best JNI tutorial site.

已经尝试过的解决方案:(但不成功)

Already tried solution: (But not success)

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

如何在jni中将jbyteArray转换为本机char *?

int len = (*env)->GetArrayLength (env , array );
printf(" Length of the bytearray %d\n", len );
unsigned char * string ;
string = (char *)malloc((len + 1) * sizeof(char)) ;

jbyte* b = (*env)->GetByteArrayElements(env, array, &isCopy);

GetByteArrayElements之后的jbyte长度应为8,但GetArrayLength返回的字节数组长度为50335.

After GetByteArrayElements jbyte length should be 8 but the GetArrayLength returns bytearray length is 50,335.

我尝试什么:

JNIEXPORT void JNICALL Java_HelloJNI_sayHello (JNIEnv *env, jobject job, jbyteArray array ){

    jsize num_bytes = (*env)->GetArrayLength(env, array);
    char *buffer = malloc(num_bytes + 1);

    printf("Number of jByte element    : %d\n", (int) num_bytes);

    if (!buffer) 
        printf("Buff Fail\n");

    jbyte* elements = (*env)->GetByteArrayElements(env, array, NULL);
    if (!elements)
        printf("Element Fail\n");

    printf ("Number of Byte elements   : %d\n", (int) strlen (elements));

    memcpy(buffer, elements, num_bytes);
    buffer[num_bytes] = 0;

    printf("Number of buffer elements  : %d\n", (int) strlen(elements));

    (*env)->ReleaseByteArrayElements(env, array, elements, JNI_ABORT);

    FILE *fp;
    fp = fopen( "file.txt" , "w" );
    fwrite(buffer , 1 , sizeof(buffer) , fp );
    fclose(fp);
    return;
}

和输出:

Done
Number of jByte element   : 50335
Number of Byte elements   : 8
Number of buffer elements : 8

推荐答案

正如@Olaf所观察到的,指针和数组完全是不同种类的对象.没有有意义的方法可以将彼此转换.因此,第一步是更好地表征您的实际意思.

As @Olaf observed, pointers and arrays are altogether different kinds of objects. There is no meaningful way to convert one to another. The first step, therefore, is to better characterize what you actually mean.

由于您的最终目标似乎是通过fputs()将Java数组的字节写入文件,因此似乎您想要做的是创建一个C字符串,其内容与Java字节数组的内容相同. .这恰恰是奥拉夫(Olaf)建议的欺骗,转换jbyteArray到字符数组,然后打印到控制台,请求以及所接受的答案旨在提供的内容.但是,您声称您已经尝试过该解决方案,但是该解决方案无效.您没有描述失败的性质,但是我可以相信它的确会失败,因为公认的答案中的解决方案有点麻烦.在下文中,我将该答案称为历史性答案".

Since your ultimate objective appears to be to write the bytes of the Java array to a file via fputs(), it seems that what you want to do is to create a C string whose contents are the same as the Java byte array's. This is exactly what Olaf's proposed dupe, Converting jbyteArray to a character array, and then printing to console, requests, and what the accepted answer purports to provide. You claim, however, that you already tried that solution and it didn't work. You don't describe the nature of the failure, but I can believe that it does fail, because the solution in the accepted answer is a bit buggy. In what follows, I'll refer to that answer as "the historic answer".

历史上的答案在几点上都是正确的.特别是,C字符串必须以空字符结尾,并且Java字节数组的内容不会终止,除非偶然.同样,jbytearray指针也不是直接指向数据的指针,因此您需要使用适当的JNI函数来获取数据本身.您需要创建一个新缓冲区,该缓冲区的大小足以容纳字节数组的内容 plus 终止符,将字节复制到其中,然后添加终止符.

The historic answer is quite correct on several points. In particular, a C string must be null-terminated, and the contents of a Java byte array are not terminated, unless accidentally. Also, the jbytearray pointer is not a pointer directly to the data, so you need to use appropriate JNI functions to get the data themselves. You'll need to create a new buffer large enough for the byte array's contents plus the terminator, copy the bytes into it, and add the terminator.

例如:

// determine the needed length and allocate a buffer for it
jsize num_bytes = GetArrayLength(env, array);
char *buffer = malloc(num_bytes + 1);

if (!buffer) {
    // handle allocation failure ...
}

// obtain the array elements
jbyte* elements = GetByteArrayElements(env, array, NULL);

if (!elements) {
    // handle JNI error ...
}

// copy the array elements into the buffer, and append a terminator
memcpy(buffer, elements, num_bytes);
buffer[num_bytes] = 0;

// Do not forget to release the element array provided by JNI:
ReleaseByteArrayElements(env, array, elements, JNI_ABORT);

之后,在buffer指向的空间中,将找到字节数组内容的以空终止的副本,您可以将其传递给fputs():

After that, you have in the space pointed to by buffer a null-terminated copy of the contents of the byte array, which you can, for example, pass to fputs():

int result = fputs(buffer, file);

此外,一旦成功分配了缓冲区,就必须确保在从函数返回之前释放它,包括通过任何错误处理返回路径:

Also, once you successfully allocate the buffer, you must be certain to free it before returning from the function, including via any error-handling return path:

free(buffer);

我从历史答案中看到的主要问题是,它建议使用strlen()计算数组数据的长度,以便能够分配足够大的临时缓冲区.但这仅在字节数组已经为null终止的情况下才有效,在这种情况下,一开始就没有必要.

The main problem I see with the historic answer is that it suggests using strlen() to compute the length of the array data so as to be able to allocate a sufficiently large temporary buffer. But that could work only if the byte array were already null terminated, in which case it wouldn't be necessary in the first place.

以上内容回答了所提出的问题-如何将数据转换为C字符串.但是请注意,问题本身是基于这样的假设:首先将数据转换为C字符串并通过fputs()输出它们是一种适当的机制.正如@Michael所观察到的,如果数据包含空字节则不是这种情况,因为很难排除它们是否最初来自二进制文件.

The above answers the question as posed -- how to convert the data to a C string. Note, however, that the question itself is premised on the supposition that converting the data to a C string and outputting them via fputs() is an appropriate mechanism in the first place. As @Michael observed, that is not the case if the data contain null bytes, as may be difficult to rule out if they originally come from a binary file.

如果总体目标只是将字节写到文件中,那么首先将它们转换为C字符串是没有意义的.有其他机制可用于在不先执行此类转换的情况下输出数据.如果可以依靠数据不包含内部空字节,则可以使用fprintf()编写它们:

If the overall objective is simply to write the bytes to a file, then first converting them to a C string is pointless. There are alternative mechanisms for outputting the data without first performing such a conversion. If the data can be relied upon not to contain internal null bytes, then you can use fprintf() to write them:

fprintf("%*s", (int) num_bytes, (char *) elements);

另一方面,如果数据可能包含空值,则应使用适当的低级输出函数.可能看起来像这样:

On the other hand, if the data may contain nulls then you should use an appropriate low-level output function. That might look like this:

#include <stdio.h>
#include <jni.h>
#include "FileIO.h"

JNIEXPORT void JNICALL Java_FileIO_writeToFile(JNIEnv *env, jobject job,
        jbyteArray array) {
    FILE *fp = fopen( "file.txt" , "w" );

    if (!fp) {
        // handle failure to open the file ...
    }

    // determine the needed length and allocate a buffer for it
    jsize num_bytes = GetArrayLength(env, array);

    // obtain the array elements
    jbyte* elements = GetByteArrayElements(env, array, NULL);

    if (!elements) {
        // handle JNI error ...
    }

    // output the data
    if (fwrite(elements, 1, num_bytes, fp) != num_bytes) {
        // handle I/O error ...
    }

    // Do not forget to release the element array provided by JNI:
    ReleaseByteArrayElements(env, array, elements, JNI_ABORT);

    fclose(fp);
}

这篇关于如何在JNI中将Java字节数组的内容转换为C字符串?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆