2014-06-29 14 views
5

Sto cercando di ordinare i caratteri, la loro rappresentazione in sequenze di byte in base ai set di caratteri e come convertire da un set di caratteri all'altro in Java. Ho delle difficoltàByteBuffer, CharBuffer, String e Charset

Per esempio,

ByteBuffer bybf = ByteBuffer.wrap("Olé".getBytes()); 

mia comprensione è che:

  • String sono sempre memorizzati come UTF-16 sequenza di byte in Java (2 byte per carattere, big endian)
  • getBytes() risultato è la stessa sequenza di byte UTF-16
  • wrap() mantiene questa sequenza
  • bybf è quindi una grande rappresentazione UTF-16 endian della stringa Olé

Così in questo codice:

Charset utf16 = Charset.forName("UTF-16"); 
CharBuffer chbf = utf16.decode(bybf); 
System.out.println(chbf); 

decode() dovrebbe

  • interpretare bybf come una stringa UTF-16 rappresentazione
  • "convert" nella stringa originale Olé.

In realtà nessun byte deve essere modificato poiché tutto è memorizzato in UTF-16 e UTF-16 Charset dovrebbe essere una sorta di "operatore neutro". Tuttavia il risultato viene stampato come:

?? 

Come può essere?

Domanda aggiuntiva: per la conversione corretta, sembra che Charset.decode(ByteBuffer bb) richieda l'bb come immagine di sequenza di byte big endian UTF-16 di una stringa. È corretto?


Edit: Dalle risposte fornite, ho fatto alcuni test per stampare un contenuto ByteBuffer e la chars ottenuto dalla decodifica. I byte [codifica con = "Olé".getBytes(charsetName)] sono stampati sulla prima riga di gruppi, le altre linee sono le stringhe ottenute decodificando i byte [con Charset#decode(ByteBuffer)] con vari Charset.

Ho anche confermato che la codifica predefinita per la memorizzazione di String in byte[] su un computer Windows 7 è windows-1252 (a meno che le stringhe non contengano caratteri che richiedono UTF-8).

Default VM encoding: windows-1252 
Sample string: "Olé" 


    getBytes() no CS provided : 79 108 233 <-- default (windows-1252), 1 byte per char 
    Decoded as windows-1252: Olé   <-- using the same CS than getBytes() 
      Decoded as UTF-16: ??   <-- using another CS (doesn't work indeed) 

    getBytes with windows-1252: 79 108 233 <-- same than getBytes() 
    Decoded as windows-1252: Olé 

     getBytes with UTF-8: 79 108 195 169 <-- 'é' in UTF-8 use 2 bytes 
      Decoded as UTF-8: Olé 

     getBytes with UTF-16: 254 255 0 79 0 108 0 233 <-- each char uses 2 bytes with UTF-16 
      Decoded as UTF-16: Olé       (254-255 is an encoding tag) 

risposta

7

Si sono per lo più corretta.

La rappresentazione del carattere nativo in java è UTF-16.Tuttavia, quando si convertono i caratteri in byte si specifica il set di caratteri che si sta utilizzando, oppure il sistema usa il valore predefinito che di solito è stato UTF-8 ogni volta che si seleziona. Ciò produrrà risultati interessanti se si mescolano e si abbinano.

ad esempio per il mio sistema il seguente

System.out.println(Charset.defaultCharset().name()); 
ByteBuffer bybf = ByteBuffer.wrap("Olé".getBytes()); 
Charset utf16 = Charset.forName("UTF-16"); 
CharBuffer chbf = utf16.decode(bybf); 
System.out.println(chbf); 
bybf = ByteBuffer.wrap("Olé".getBytes(utf16)); 
chbf = utf16.decode(bybf); 
System.out.println(chbf); 

produce

UTF-8
佬 쎩
Olé

Quindi questa parte è corretto solo se UTF-16 è l'impostazione predefinita charset
getBytes() result is this same UTF-16 byte sequence.

Quindi, specificare sempre il set di caratteri che si sta utilizzando, che è più sicuro in quanto saprete sempre cosa sta succedendo o utilizzare sempre il valore predefinito.

+1

la maggior parte dei sistemi Windows non esegue il comando _not_ in modo predefinito su utf-8. Inoltre, non sono sicuro di cosa intendi con "UTF-16 ish". java utilizza UTF-16. – jtahlborn

+0

Grazie BevynQ. Attualmente sto imparando Java, la tua dimostrazione mi è stata molto utile. – mins

+1

@jtahlborn: il mio CS predefinito era Windows-1252 fino a quando ho cambiato la stringa di esempio in "I ♥ café". Aggiungendo il cuore reso Java passare a UTF-8. Molto educativo. – mins

7

String sono sempre memorizzati come UTF-16 sequenza di byte in Java (2 byte per carattere, big endian)

Sì.

getBytes() risultato è lo stesso UTF-16 sequenza di byte

No. codifica i UTF-16 caratteri nel charset piattaforma predefinita, qualunque essa sia. Deprecato.

involucro() mantiene questa sequenza

wrap() mantiene tutto.

bybf è quindi una rappresentazione endian big UTF-16 della stringa Olé

No. avvolge codifica predefinita della piattaforma della stringa originale.

decode()

  • dovrebbe interpretare bybf come rappresentazione di stringa UTF-16

No, vedi sopra.

  • "convertirlo" nella stringa originale Olé.

No, a meno codifica di default della piattaforma è "UTF-16".

+1

Grazie per la risposta molto dettagliata. L'avrei selezionato come corretto anche se fosse possibile selezionare più risposte. [getBytes()] (http://docs.oracle.com/javase/8/docs/api/java/lang/String.html#getBytes--) non è ancora deprecato, sebbene sia scoraggiato. – mins

+0

@mins [String.getBytes()] (https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#getBytes--) è in effetti deprecato. Vedi il Javadoc. Ci sono un paio di sovraccarichi che non lo sono, ma non li stai usando. – EJP

+1

@EJP L'unico #getBytes() che è deprecato è ['public void getBytes (int srcBegin, int srcEnd, byte [] dst, int dstBegin)'] (https://docs.oracle.com/javase/8/ docs/api/java/lang/String.html # getBytes-int-int-byte: A-int-), tutte le altre versioni sovraccaricate di questo metodo (incluso quello senza argomenti) non sono deprecate. – klaar

0

Avevo quasi lo stesso problema con i dati codificati nel set di caratteri doublebyte. La risposta 3 sopra contiene già le insidie ​​critiche da tenere d'occhio.

  1. Definire un set di caratteri per la codifica di origine.
  2. Definire un set di caratteri solo per la codifica di destinazione se è diverso dalla codifica del sistema locale.

Seguendo codice funziona

public static String convertUTF16ToString(byte[] doc) 
{ 
    final Charset doublebyte = StandardCharsets.UTF_16; 
    // Don't need this because it is my local (system default). 
    //final Charset ansiCharset = StandardCharsets.ISO_8859_1; 

    final CharBuffer encoded = doublebyte.decode(ByteBuffer.wrap(doc)); 
    StringBuffer sb = new StringBuffer(encoded); 
    return sb.toString();   
} 

Sostituire default del sistema per la codifica preferito.

public static String convertUTF16ToUTF8(byte[] doc) 
{ 
    final Charset doublebyte = StandardCharsets.UTF_16; 
    final Charset utfCharset = StandardCharsets.UTF_8; 
    final Charset ansiCharset = StandardCharsets.ISO_8859_1; 

    final CharBuffer encoded1 = doublebyte.decode(ByteBuffer.wrap(doc)); 
    StringBuffer sb = new StringBuffer(encoded1); 
    final byte[] result = ansiCharset.encode(encoded1).array(); 
    // alternative to utf-8 
    //final byte[] result = utfCharset.encode(encoded1).array(); 

    return new String(result);   
} 
+2

'Risposta 3' non ha significato. Si prega di fornire un autore o un link. – EJP