JNI GetByteArrayElements () ошибка


Я новичок в JNI, поэтому я не знаком с JNI, а также с английским языком.

Мой проект JNI-это простое чтение и запись файлов. Чтение файла в Java и передача массива байтов в C API запись его в файл с помощью C.

Мой исходный код:

Код Java:

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

   private native void writeFile(byte[] msg);

   public static void main(String[] args) throws IOException { 

      byte[] array = Files.readAllBytes(new File("PtFBWCTn.mp3").toPath());

      // System.out.println(array.length);
      new FileIO(). writeFile(array); 
   }
}

Код C:

#include <jni.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "HelloJNI.h"

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

    jsize num_bytes = (*env)->GetArrayLength(env, array);
    printf("Byte length : %dn" , num_bytes);

    unsigned char * buffer;
    jbyte  *lib ;
    lib =(jbyte *) malloc( ( num_bytes +1 ) * sizeof(jbyte));

    (*env)->GetByteArrayRegion(env , array, 0 , num_bytes , lib);
    // lib = (*env)->GetByteArrayElements(env , array, 0);
    buffer =(char *) lib ;
    FILE *fp;
    fp=fopen("test.mp3", "wb");
    printf("size : %d , length : %d  ,contant : %sn" ,(int)sizeof(buffer), (int)strlen(buffer) ,  buffer );

    fwrite(buffer, sizeof(buffer[0]), sizeof(buffer)/sizeof(buffer[0]), fp);
    return;
}

У меня проблема, пока я прохожу мимо (.zip , .mp3 , .mp4 , .jpg и .png) файловый массив байтов. Я пробую некоторые форматы текстовых файлов, такие как .txt, .java , .c файлы создаются чего я и ожидал.

В чем причина?

Я попробовал (читать мой java-файл (FileIO.java) в Java и выводом является):

Byte length : 238653
size : 8 , length : 4  ,contant : import java.nio.file.Files;
import java.io.File;

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

   private native void writeFile(byte[] msg);

   public static void main(String[] args) throws IOException { 

      byte[] array = Files.readAllBytes(new File("PtFBWCTn.mp3").toPath());

      // System.out.println(array.length);
      new FileIO(). writeFile(array); 
   }
}

И я попробовал (какой-то тест.png и вывод):

Byte length : 381729
size : 8 , length : 8  ,contant : ?PNG

GetByteArrayElements () возвращает только 8 байт при чтении носителя и другого некоторого формата. Но в случае текстового файла он возвращает правильное количество байт. Пожалуйста, укажите причину, по которой длина " png " и других некоторых форматов длины не равна длине байта. И обеспечить способ, как решить эту проблему.

3 4

3 ответа:

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

    buffer = (char *) malloc (num_bytes);

    jbyte  *lib = (*env)->GetByteArrayElements(env , array, 0);

    memcpy ( buffer , lib , num_bytes ) ;

    FILE *fp;
    fp=fopen("test.png", "wb");
    fwrite(buffer, sizeof(char) , num_bytes , fp);

Прежде всего, вы не должны выделять дополнительный байт для буфера назначения:

lib =(jbyte *) malloc( ( num_bytes +1 ) * sizeof(jbyte));
В этом нет необходимости. Также вы можете опустить sizeof(jbyte), так как это всегда 1:
lib = (jbyte *) malloc(num_bytes);

Затем:

printf("size : %d , length : %d  ,contant : %s\n" ,(int)sizeof(buffer), (int)strlen(buffer) ,  buffer );

(int)sizeof(buffer) это размер переменной указателя, а не размер адресованных данных, и программа справедливо сообщает вам, что указатель является объектом длиной 8 байт (вы находитесь на 64-битном хосте). Далее, (int)strlen(buffer) не возвращает фактический размер указанных данных, вместо этого он возвращает количество байт, предшествующих первому нулю ('\0') байт в ваших данных.

И из - за этого strlen() результат совпадает с длиной данных, которые были считаны из текстовых файлов-они почти всегда не содержат нулевых байт. А двоичные файлы, такие как PNG, MP3 и так далее, часто содержат ноль байт, и фактическая длина не совпадает с результатом strlen().

В довершение всего: единственная истинная длина данных - это результат GetArrayLength().

EDIT

Фактические аргументы для fwrite() не в порядке:

fwrite(buffer, sizeof(buffer[0]), sizeof(buffer)/sizeof(buffer[0]), fp);

sizeof(buffer)/sizeof(buffer[0] есть то же самое, что и 8/1, поскольку buffer является 8-байтовым указателем длины, а buffer[0] является unsigned char, то есть сущностью длиной в один байт. Вы должны переписать его так:

fwrite(buffer, 1, num_bytes, fp);

И нет никакой стандартной функции, которая может помочь вам распечатать все данные, включая нули. Я думаю, что вы должны вручную перебирать каждый байт, форматировать и печатать.

Как вы можете видеть человек говорит:

Описание

Функция strlen() вычисляет длину строки, на которую указывает по s, исключая завершающий нулевой байт ('\0').

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Функция strlen () возвращает количество символов в строке на что указывает С.

Это означает, что вы не можете открыть чистый двоичный файл и притвориться, что у него есть длина содержания, использующего эту функцию, из-за нее прекращается подсчет символов при первом null terminator. Другими словами, он прекращает отсчет при первом байте, установленном в 0.

Очевидно, что он работает с текстовым файлом, который закодирован для обеспечения отсутствия null terminator между словами.

Вторая ошибка - (int)sizeof(buffer) она возвращает размер в байтах объектного представления типа, который в вашем случае является pointer к unsigned char. На вашей платформе (64 бит..) указатели имеют длину 8 байт.

Примечание сбоку: вы можете избежать, чтобы бросить возвращает значение функции, возвращающей тип size_t с помощью спецификатора формата "%zu".

printf("size : %zu , length : %zu  ,contant : %s\n" , sizeof(buffer), strlen(buffer) ,  buffer );

EDIT

Вы не можете получить размер прочитанных байтов таким образом.

Не существует стандартной функции, которая может получить размер динамически выделенного массива.

Вы должны написать свой собственный код. Например, для файла png вы можете получить длину через заголовок файла. Взгляните на PNG спецификации