2013-07-05 14 views
6

Piszę program JNI, w którym mój plik .cpp otrzymuje wartość jbyteArray i chcę móc wydrukować jbyteArray z printf. Aby tak się stało, uważam, że muszę przekonwertować jbyteArray na tablicę znaków.Konwertowanie jbyteArray na tablicę znaków, a następnie drukowanie na konsolę

Dla wiedzy w tle, strona java mojego JNI konwertuje String na byteArray, a następnie byteArray jest przekazywany jako argument do mojej funkcji JNI.

Co zrobiłem do tej pory poprawnie wypisuje łańcuch, ale za nim pojawiają się postacie ze śmieciami i nie wiem, jak się ich pozbyć/jeśli robię coś złego.

Oto co String jest:

dsa 

i co drukuje się pocieszyć:

dsa,� 

znaków śmieci zmieniać w zależności od tego, co jest String. Oto część kodu, która jest istotna:

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

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

Każda pomoc będzie mile widziane.

Odpowiedz

7

Spójrz, co robisz:

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

a wskazuje teraz do adresu pamięci, gdzie przechowywane są treści bajt łańcucha. Załóżmy, że plik zawiera ciąg "Hello world". W kodowaniu UTF-8, która wynosi:

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

char* b = (char*)a; 

b wskazuje teraz na tym obszarze pamięci. Jest to wskaźnik char, więc prawdopodobnie chcesz go użyć jako ciąg C. Jednak to nie zadziała. Łańcuchy C są definiowane jako niektóre bajty, kończąc na bajcie zerowym. Teraz spójrz tam i zobaczysz, że na końcu tego ciągu nie ma zerowego bajtu.

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

Oto ona. Przekazujesz wskaźnik znaku do printf jako %s, który mówi printf, że jest ciągiem C. Jednak nie jest to ciąg znaków C, ale printf nadal próbuje wydrukować wszystkie znaki, aż osiągnie zero bajtów. Więc widzisz po dsa są to bajty z twojej pamięci po końcu tablicy bajtów, aż pojawi się (przez przypadek) zero bajtów. Możesz to naprawić, kopiując bajty do bufora o jeden bajt dłuższego niż tablica bajtów, a następnie ustawiając ostatni element na zero.

UPDATE:

Można utworzyć większy bufor i dołącz zerowy bajt tak:

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

Teraz b jest prawidłowy C ciąg zakończony zerem. Nie zapomnij też o połączeniu z numerem ReleaseByteArrayElements. Możesz to zrobić zaraz po wywołaniu memcpy.

+0

Przepraszam, jeśli to brzmi jak bardzo nowicjusz, ale jak mam zrobić bufor, który jest o jeden bajt dłuższy, a potem jak mam przejść do edycji ostatniego elementu? –

+0

@SeanSenWang Zobacz moją edycję. –

+0

dzięki, działał jak urok. Niewielka edycja, jestem w C++ nie C, więc muszę rzucić (char *), gdy wywołanie malloc jest wykonane. –

2

JbyteArray jest w rzeczywistości bardzo dobrym sposobem przekazania ciągu Java za pośrednictwem JNI. Pozwala to w łatwy sposób przekonwertować ciąg znaków na zestaw znaków i kodowanie wymagane przez biblioteki i pliki/urządzenia używane po stronie C++.

Zanim zrozumiesz "The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)"

Java String wykorzystuje zestaw znaków Unicode i UTF-16 kodowania (z platformą zależne Byte Order).

String.getBytes() konwertuje na "domyślny zestaw znaków platformy". Tak więc przyjmuje założenie dotyczące zestawu znaków i kodowania, których potrzebujesz i co zrobić z postaciami, których nie ma w docelowym zestawie znaków. Możesz użyć innych przeciążeń Java String.getBytes lub metod Charset, jeśli chcesz jawnie kontrolować te rzeczy.

Podejmując decyzję, który zestaw znaków i kodowanie należy zastosować, należy wziąć pod uwagę, że Unicode był używany przez kilka dekad jako podstawowy typ ciągu w języku Java, .NET, VB, ...; w źródłowych plikach kompilatora dla Javy, ...; ogólnie w WWW. Oczywiście, możesz być ograniczony przez rzeczy, z którymi chcesz współpracować.

Wygląda na to, że problemem jest to, że w docelowym zestawie znaków brakuje znaków, które ma ciąg Java i używany jest zamiennik, lub że konsola, której używasz, nie wyświetla ich poprawnie.

Konsola (lub jakaś aplikacja z interfejsem użytkownika) oczywiście musi wybrać krój pisma, który będzie renderował znaki. Kroje pism zwykle nie obsługują milionów punktów kodowych dostępnych w Unicode. Możesz mieć możliwość zmiany konfiguracji konsoli (lub użycia innej). Na przykład w systemie Windows można użyć programu cmd.exe lub ps (Windows PowerShell). Możesz zmienić czcionkę w oknach programu Cmd.exe i użyć chcp, aby zmienić zestaw znaków.

UPDATE:

Jako @ main-- zwraca uwagę, jeśli używasz funkcji, które spodziewa się terminator dołączany do łańcucha potem trzeba je podać, zazwyczaj poprzez kopiowanie tablicę ponieważ JVM zachowuje prawo własności do szyk. Jest to faktyczna przyczyna zachowania w tym przypadku. Ale wszystkie powyższe są również istotne.

+0

To nie jest problem w tym przypadku. Spójrz na moją odpowiedź. –

Powiązane problemy